Compare commits

...

56 Commits

Author SHA1 Message Date
funman300 063269c70e docs: update repo URL references to corrected Rusty_Solitaire spelling
The GitHub repo was renamed from Rusty_Solitare to Rusty_Solitaire
(adding the missing 'i'). The local origin remote has been updated
via `git remote set-url`; this commit updates the three doc
references that hardcoded the old URL.

SESSION_HANDOFF.md's "Canonical remote" section now names the new
URL and explains the rename for future readers, including the note
that local clone directories may still be named Rusty_Solitare —
that's a local-only name and works fine, only the GitHub repo URL
changed.

docs/SESSION_HANDOFF.md (older snapshot, unchanged otherwise) gets
its single URL line corrected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:36:06 +00:00
funman300 b126df82b2 docs: refresh SESSION_HANDOFF for session 7 UX-iteration round complete
Session 6 closed with a four-item UX punch list (unlock foundations,
drop shadows, drop-target highlights, stock badge). All four shipped
in session 7, plus an unrelated font-fallback fix surfaced by a
second-machine smoke test that landed before the UX work.

Refreshes the doc to reflect:
- HEAD: 655dfde, 3 commits ahead of origin
- 982 tests pass (was 962)
- Session 7 changelog table summarising the five commits
- UX punch-list entirely closed; release-prep items still on the
  table but un-deferred (player gets a directional choice next session)
- New "next-round candidates" UX list (animated focus ring,
  achievement onboarding, mode-switch keyboard shortcut, aspect-ratio
  fidelity, foundation completion flourish, drag-cancel tween)
- Resume prompt asks A/B/C: tag v0.11.0, README/CHANGELOG first, or
  start a new UX round

Length 120 → 109 (-11) by trimming the spent priority list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:33:42 +00:00
funman300 655dfde736 feat(engine): stock-pile remaining-count badge
Players were recycling the stock blind — there's no in-world
indicator of how many cards are left before the recycle. A small
"·N" chip now sits at the top-right corner of the stock pile,
showing the remaining count.

The badge is a top-level world entity whose Transform.translation is
recomputed each tick from the live LayoutResource (so window resizes
and theme switches don't strand it), parented to neither the
PileMarker nor any card. update_stock_count_badge spawns the entity
on the first frame, then on every subsequent frame reads the stock
pile's card count, writes the formatted text into the child Text2d,
and toggles Visibility::Hidden when the count drops to zero — the
same state where StockEmptyLabel's existing ↺ icon takes over, so
the two never co-render.

Z_STOCK_BADGE = 30 sits above stock cards (z ≈ 1) and below
Z_DROP_OVERLAY = 50, so the badge stays visible during normal play
but green drop-target washes still cover it while a card is being
dragged. Card drop shadows live at negative local z relative to
each card and don't compete with the badge plane.

Tokens (STOCK_BADGE_BG, STOCK_BADGE_FG, Z_STOCK_BADGE) were already
present in ui_theme from prior work; this commit only wires them up.
The chip itself is 28×16 px, rendered with TYPE_CAPTION text in
ACCENT_PRIMARY against BG_ELEVATED_HI.

Four new tests pin the contract: badge shows "·24" on a fresh deal,
hides when the stock empties, updates as the count drops, and the
stock_card_count helper reports 0 when the pile is missing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:31:15 +00:00
funman300 f712b89fe4 feat(engine): drop shadows on cards with lifted state during drag
Cards previously read as flat stickers on the felt — no separation
cue, no sense the play surface had any depth. Each CardEntity now
spawns a CardShadow child sprite: neutral black at 25 % alpha, sized
to card_size + 4 px halo, offset (2, -3) and rendered at local z
-0.05 so it sits behind its card.

Cards in the active drag set switch to a lifted shadow: alpha 40 %,
offset (4, -6), padding (8, 8). update_card_shadows_on_drag runs
every Update and snaps each shadow to the right state based on
DragState membership — no lerp, no animation cost. The pure
card_shadow_params(is_dragged) helper is unit-tested for the four
parameter values.

resize_cards_in_place gains a third query for shadows so the
in-place resize keeps shadows cheap (no Sprite regeneration); the
shadow's current alpha is read to preserve idle vs lifted padding
across a resize. update_card_entity's despawn_related call is
followed by a fresh add_card_shadow_child so the shadow re-attaches
when the card is repainted (face flip, settings change, theme
swap). The pre-existing bulk drag-shadow under the whole lifted
stack is untouched — per-card shadows complement it.

All shadow values flow through eight new ui_theme tokens
(CARD_SHADOW_COLOR, alphas, offsets, paddings, local z) so the
visual is tunable in one place. Color is neutral black so the
shadows don't conflict with color-blind mode's red/blue suit tints.

Four new tests pin the contract: shadow params for idle and drag
states, every CardEntity spawns with exactly one CardShadow child,
and dragging shifts only the dragged shadow's offset while leaving
unrelated shadows on the idle offset.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:21:28 +00:00
funman300 f6c916641a feat(engine): visible drop-target overlay during drag
The existing update_drop_highlights system tinted PileMarker sprites
green for valid drops, but the marker is a card-sized rectangle that
sits behind the stack. Once a tableau column had any cards on it the
marker was occluded and the highlight effectively invisible — the
handoff's "drops feel guess-y because there's no preview" point.

A new update_drop_target_overlays system spawns an overlay above every
legal target during drag: a soft DROP_TARGET_FILL rectangle sized to
the pile's actual visible footprint (full fanned column for tableaux,
card-sized for foundations and empty tableaux) plus four thin
DROP_TARGET_OUTLINE edges forming a 3 px border. Z_DROP_OVERLAY = 50
sits above static cards (z ~1) but below the dragged stack (DRAG_Z =
500), so the overlay never occludes the card the player is holding.

The valid-target enumeration mirrors update_drop_highlights exactly so
the rules can't drift, and pile geometry mirrors input_plugin's
pile_drop_rect. The original marker-tint system is untouched; it still
does its job for empty-pile placeholders. The overlay layer is purely
additive — running alongside, not replacing.

Token values reuse the existing STATE_SUCCESS hue (#4ADE80) at 10%
fill / 75% outline so the overlay green matches the rest of the
success-signal palette (foundation completion, sync OK, etc.).

Three headless tests pin the contract: overlay spawns for valid
tableau drops, doesn't spawn for invalid destinations, and despawns
the moment the drag ends.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 22:33:22 +00:00
funman300 95df5421c9 feat(core): unlock foundations — Foundation(u8) slots, suit derived from contents
Standard Klondike behaviour: any Ace can land in any empty foundation,
and that slot then claims the suit until the pile empties. The
previous PileType::Foundation(Suit) variant pre-assigned each of the
four foundations to a fixed suit ("C / D / H / S" placeholders) and
rejected mismatched Aces — non-standard and (per the smoke-test
feedback) confusing.

Replaces the variant payload with a slot index Foundation(u8) (0..=3)
and derives the claimed suit from the bottom card via a new
Pile::claimed_suit() method. The bottom card is, by construction,
the Ace that established the claim; using it directly eliminates an
entire class of "stuck claim after undo" bugs that a separate
claimed_suit field would have introduced.

can_place_on_foundation drops its suit parameter — the rule reduces
to "empty pile accepts any Ace; non-empty pile accepts the next
rank up of the bottom card's suit." Iteration sites across
input_plugin, cursor_plugin, selection_plugin, card_plugin,
auto_complete_plugin, game_plugin, layout, and hud_plugin all swap
the four-suit list for `(0..4u8).map(PileType::Foundation)`.

next_auto_complete_move now prefers a slot whose claimed_suit matches
the candidate card before falling back to the first empty slot for
an Ace — so the same suit consistently auto-targets the same slot
across the whole game, matching player expectations.

The HUD selection label and the hint toast read claimed_suit() and
fall back to "Foundation N" / "move to foundation" only when the
slot is empty. Empty foundation pile markers no longer render the
suit-letter children — they're plain translucent rectangles, matching
empty tableau placeholders.

Save-format invalidation: GameState gains a schema_version field
(serde-default to 1 for back-compat parsing of old files), the
constant is bumped to 2, and load_game_state_from rejects mismatched
schemas. Old in-progress saves silently fall through to "fresh game
on launch" — the user accepted this loss given the mechanic change.
Stats / progress / achievements / settings live in separate files,
contain no PileType data, and are unaffected.

9 new tests pin the contract:
- Pile::claimed_suit returns None for empty / non-foundation, Some
  for non-empty foundation
- Any Ace lands in the first empty foundation; successive Aces
  distribute across slots 0..3
- Claim drops when the slot is emptied via undo
- Auto-complete picks the slot with a matching claim, not the first
  empty slot
- A v1-format game_state.json is rejected; sibling stats save/load
  is unaffected

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 22:17:17 +00:00
funman300 fdb6c2ecfe fix(engine): bundle FiraMono into SVG fontdb as last-resort fallback
The hayeah card SVGs reference Bitstream Vera Sans and Arial by name.
The lenient FontResolver from efa063f appends Family::SansSerif and
Family::Serif so unmatched named families fall through to whatever
the system serves under those CSS generics — which works on machines
with a normal fontconfig setup, and silently fails on minimal Linux
installs, fresh Wayland sessions, or chroots where the generic
aliases don't resolve to anything either. The visible symptom on the
player's second machine was "card font didn't carry over": rank and
suit glyphs vanished from the cards because every lookup path hit a
None.

shared_fontdb now also include_bytes!()s the bundled
assets/fonts/main.ttf into the fontdb after load_system_fonts, and
pins each CSS generic (sans-serif, serif, monospace, cursive,
fantasy) to "Fira Mono". Named-family lookups still prefer the
system db first when those families exist, so machines with a normal
font setup behave identically; only when SansSerif/Serif fall through
does the resolver land on FiraMono — guaranteed present because it's
embedded in the binary.

The bundled font is ~170 KB; the binary already include_bytes!()s the
six audio WAVs and the embedded card-theme SVGs, so this fits the
existing self-contained-binary policy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 21:41:35 +00:00
funman300 9a3d7f3876 docs: refresh SESSION_HANDOFF for session 6 + UX-iteration direction
Captures today's six commits (theme loader fix, exit-warn silence, two
font-warn rounds, HUD band, action fade), updates HEAD/test counts,
records that the player redirected from "cut v0.11.0 / package" to
"keep iterating on UX," and lists the new four-item UX punch list
(unlock foundations, drop shadows, drop highlighting, stock badge).

Resume prompt is rewritten so a fresh agent on a different machine
picks up cleanly: notes GitHub is the canonical remote (Gitea drift
caused commits to silently miss the alex machine earlier in session),
flags that the in-progress save format will invalidate when (1)
lands, and explicitly defers the release-prep items.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 21:24:09 +00:00
funman300 c4970b16ea feat(engine): auto-fade HUD action buttons when cursor leaves the band
Player request: the Menu / Undo / Pause / Help / Modes / New Game
buttons stay visible during play even when the player isn't looking
at them. Fade them out when the cursor is in the play area, fade
back in when it returns to the top of the window.

Implementation mirrors video-player auto-hide UX:
- HudActionFade resource holds (alpha, target). Default both 1.0 so
  the bar starts visible on first launch.
- update_action_fade reads cursor.y each frame, sets target to 1.0
  when the cursor is in the top reveal zone (HUD_BAND_HEIGHT + 32 px)
  or off-window (keyboard navigation), 0.0 otherwise. Lerps alpha
  toward target at 6/sec ≈ 167 ms per full transition.
- apply_action_fade overrides BackgroundColor + child TextColor on
  every ActionButton. Runs in Last so a hover-state change in the
  same frame doesn't blip back to opaque mid-fade.

No interactivity guard needed: hover requires the cursor to be on a
button, and a faded button is geometrically out of reach (cursor must
re-enter the reveal zone, which is exactly the trigger that fades
the bar back in).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:08:39 +00:00
funman300 2c72e1fc87 feat(engine): reserve top band for HUD so it stops crowding the cards
Player report: the action button bar (Menu / Undo / Pause / Help /
Modes / New Game) and Score / Moves / Timer text were sharing the
same vertical band as the stock + foundation row, with no visual
separation. The HUD read as part of the play surface.

Two-part fix:

1. layout.rs reserves HUD_BAND_HEIGHT (64 px) at the top of the
   window. Card-grid math takes that off the available vertical
   budget so cards still fit; top_y shifts down by the same amount.
   New layout test pins the reservation. Existing
   worst_case_tableau_fits_vertically tests verify the height-budget
   arithmetic still holds.

2. hud_plugin.rs spawns a translucent purple band (BG_HUD_BAND, new
   token in ui_theme.rs at the BG_BASE hue with 0.70 alpha) filling
   that reserved zone. Z-index sits one rung below Z_HUD so action
   buttons paint on top while the band reads as their container. The
   band's bottom edge lines up with the top edge of the highest
   playable card, so the buttons feel anchored to a "tools strip"
   rather than floating in the play area.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:57:51 +00:00
funman300 efa063fb8f fix(engine): fall through to system default font on unmatched family
CI / Test & Lint (push) Failing after 5s
CI / Release Build (push) Has been skipped
Replaces the previous LogPlugin-filter approach (which suppresses the
warn message) with a fix at the source: a custom usvg FontResolver
that appends `sans-serif` and `serif` to every family-lookup query.

usvg's default selector queries fontdb with [SVG-requested families,
Serif] and emits `log::warn!("No match for '{family}'")` when the
query returns None. On systems without the SVG's named family (Arial
on Linux, etc.), every text node logs a warn even though the system
has perfectly good fonts available — the warn is a false negative
because fontdb's named-family lookup is exact-match only.

The new resolver appends both `Family::SansSerif` and `Family::Serif`
to the query, both resolved by fontdb (via fontconfig on Linux or
built-in defaults elsewhere) to whatever the system has installed.
The query now finds *some* face on any reasonably configured machine,
so `id.is_none()` is never true and the warn branch never fires. The
visible behaviour: SVGs that request unavailable named families now
silently use the system's default sans-serif font.

Reverts the LogPlugin filter from main.rs — silencing warns at the
log level was the wrong layer; fixing the lookup is.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:41:02 +00:00
funman300 78cf30e906 fix(engine): silence usvg font-substitution warn spam
CI / Test & Lint (push) Failing after 6s
CI / Release Build (push) Has been skipped
The bundled hayeah card SVGs declare font-family="Arial" for rank/suit
text. usvg matches family names exactly, so on systems without Arial
installed (every Linux distro by default) every text node bridged a
log::warn! into our tracing output — 50+ lines per launch.

Two-part fix:
- svg_loader now populates a process-wide fontdb with system fonts
  (lazy via OnceLock) so substitution actually has faces to fall
  through to. usvg::Options::default() ships an empty fontdb, which
  meant text glyphs had nothing to fall back on at all.
- LogPlugin extends DEFAULT_FILTER with usvg::text=error so the
  residual "no match" warns drop. The substitution itself works; the
  message is purely informational because Arial truly isn't installed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:22:32 +00:00
funman300 9a9026e33a fix(engine): silence benign UnsupportedPlatform warn on exit
CI / Test & Lint (push) Failing after 4s
CI / Release Build (push) Has been skipped
push_on_exit logged every error including LocalOnlyProvider's expected
UnsupportedPlatform response, producing a misleading "sync push on exit
failed" warning on every shutdown in local-only mode. Mirror the pull
path: treat UnsupportedPlatform as silent no-op, warn only on real
errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:04:56 +00:00
funman300 ab1d098877 fix(engine): use resolve_embed for sibling theme assets
CI / Test & Lint (push) Failing after 5s
CI / Release Build (push) Has been skipped
`AssetPath::resolve` concatenates, so manifest-relative SVG paths
ended up under `…/theme.ron/<name>.svg` and the asset server
reported all 53 references missing. `resolve_embed` is the RFC 1808
sibling-resolution method that strips the base path's last segment
first, giving the intended `…/<name>.svg`. Default theme now loads
cleanly from the embedded:// source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:21:03 +00:00
funman300 160637d1c8 docs: update remote URL reference to github.com/funman300/Rusty_Solitare
CI / Test & Lint (push) Failing after 8s
CI / Release Build (push) Has been skipped
Mirrors the move of the canonical remote from git.aleshym.co to
GitHub. The git remote itself was switched via 'git remote set-url
origin'; this updates the one stale URL in docs/SESSION_HANDOFF.md
that named the old host.
2026-05-01 17:11:55 +00:00
funman300 43f13c615e chore: workspace cleanup after card-theme phase landings
Drops dead deps and stale doc content carried over from the pre-MIT
art swap.

Cargo.toml manifests:
- solitaire_core no longer depends on chrono (no source references it
  since the original sync-payload timestamps moved to solitaire_data).
- solitaire_sync no longer depends on serde_json (the sync types use
  serde-derive with whatever serializer the caller picks; the old
  json-specific helpers were removed earlier).

Cargo.lock pruned by `cargo build` to drop the now-untransitively-
referenced versions.

CREDITS.md redistribution clause: "LGPL and OFL notices" tightened to
"MIT (project + hayeah card art) and OFL (FiraMono)" since the LGPL
art is gone.

SESSION_HANDOFF.md:
- HEAD bumped to 924a1e2; test count to 960; 9 ignored.
- Punch list rewritten — the xCards-URL line is obsolete (we did the
  swap), v0.1.0 tag exists locally, and player smoke-test is the
  current top item.
- New "Card-theme system (CARD_PLAN.md, fully shipped)" section
  summarises the seven-phase end-to-end flow so a future session has
  the integration map without re-reading the plan.
- Optional list gains the SVG-vs-layout aspect-ratio note as a
  cosmetic-only follow-up.

Removed the locked worktree at .claude/worktrees/agent-aa55a94d18c669d70
left behind by a prior Claude session.

cargo build / clippy --workspace --all-targets -- -D warnings / test
--workspace all green (960 passed, 0 failed, 9 ignored).
2026-05-01 16:41:53 +00:00
funman300 924a1e2af7 feat(engine): card-theme picker in Settings → Cosmetic
CI / Test & Lint (push) Failing after 5s
CI / Release Build (push) Has been skipped
Wires the runtime theme system (CARD_PLAN.md phases 1–7) into the
visible Settings UI so a player can switch between every theme
discovered by `ThemeRegistry` without restarting.

solitaire_data/src/settings.rs
  Settings gains `selected_theme_id: String` (default "default"),
  guarded by `#[serde(default = "default_theme_id")]` so existing
  settings.json files deserialize cleanly.

solitaire_engine/src/settings_plugin.rs
  - SettingsButton::SelectTheme(String) variant + focus order 85.
  - sync_settings_panel_visibility now reads
    Option<Res<ThemeRegistry>>, snapshots id+display_name pairs, and
    threads them into spawn_settings_panel. When the registry is
    absent (tests under MinimalPlugins) the picker silently skips —
    every existing test continues to pass unchanged.
  - theme_picker_row helper: like picker_row but keyed by String
    rather than usize, with chips wide enough for theme display
    names. Attaches the canonical tooltip ("Choose card-face
    artwork. Imported themes appear here.") and the FocusRow marker
    so Left/Right arrows cycle within the row.
  - Click handler updates settings.selected_theme_id, persists, and
    fires SettingsChangedEvent — same shape as every other picker.

solitaire_engine/src/theme/plugin.rs
  - load_default_theme renamed to load_initial_theme; reads
    SettingsResource on Startup and seeds ActiveTheme from
    settings.selected_theme_id (falling back to embedded default).
  - react_to_settings_theme_change watches SettingsChangedEvent,
    no-ops when the active theme already matches, and otherwise
    swaps ActiveTheme — the existing
    sync_card_image_set_with_active_theme system then refreshes
    every card sprite on the next AssetEvent::LoadedWithDependencies.

cargo build / clippy --workspace --all-targets -- -D warnings / test
--workspace all green (960 passed, 0 failed, 9 ignored).
2026-05-01 16:24:24 +00:00
funman300 a6b8348332 docs: refresh README + ARCHITECTURE for hayeah art + theme system
CI / Test & Lint (push) Failing after 7s
CI / Release Build (push) Has been skipped
Updates the prose mentions of card-face provenance to point at
hayeah/playing-cards-assets (MIT) instead of xCards (LGPL-3.0), in
sync with the upstream art swap (b98cb8a).

ARCHITECTURE.md decision log gains two new rows: the licence-driven
art swap and the runtime SVG card-theme system landed across
CARD_PLAN.md phases 1–7. README.md credits paragraph rewritten to
match the new attribution.
2026-05-01 16:08:14 +00:00
funman300 b98cb8a99f feat(assets): swap card art to hayeah/playing-cards-assets (MIT)
Replaces the previous xCards-derived card faces (LGPL-3.0) with
hayeah/playing-cards-assets, which itself derives from the
public-domain vector-playing-cards Google Code project. The whole
package is MIT now — see CREDITS.md for the new attribution table
and the simpler license summary.

solitaire_engine/assets/themes/default/
  52 face SVGs (clubs/diamonds/hearts/spades × ace/2-10/jack/queen/
  king) — copied from hayeah, renamed to the canonical
  `{suit}_{rank}.svg` form `CardKey::manifest_name` produces. The
  bundled default theme manifest references each by the same name.
  back.svg — original midnight-purple-themed card back, hand-written
  to match the project's design tokens (BG_BASE / BG_ELEVATED /
  ACCENT_PRIMARY / ACCENT_SECONDARY). MIT, original work.

assets/cards/faces/{RANK}{SUIT}.png
  52 PNGs regenerated from the new SVGs at 750-tall via resvg 0.47.
  These remain the legacy backwards-compat path that
  `card_plugin::load_card_images` reads at startup; once the runtime
  theme system finishes loading the embedded default theme, the
  CardImageSet's face handles are overwritten with the SVG-rendered
  variants and these PNGs become moot. Keeping them in place avoids
  a brief blank-card flash before the async theme load completes.

solitaire_engine/src/assets/sources.rs
  embed_default_svg!() macro + DEFAULT_THEME_SVGS table that bundles
  every face + the back into the binary at compile time via
  include_bytes!. populate_embedded_default_theme now iterates the
  table so the EmbeddedAssetRegistry is populated under the same
  asset paths the manifest references.

CREDITS.md
  License summary collapses from MIT + LGPL-3.0 + OFL-1.1 to MIT +
  OFL-1.1 (the OFL still applies to FiraMono). The hayeah upstream
  URL replaces the previously-blank xCards entry.

cargo build / clippy --workspace --all-targets -- -D warnings / test
--workspace all green (960 passed, 0 failed, 9 ignored).
2026-05-01 16:06:58 +00:00
funman300 7b59e70192 feat(engine): theme registry + discovery (Card theme phase 6)
CI / Test & Lint (push) Failing after 8s
CI / Release Build (push) Has been skipped
Implements Phase 6 of CARD_PLAN.md — discovers every available card
theme on startup so the future picker UI can list them.

solitaire_engine/src/theme/registry.rs
  ThemeEntry { id, display_name, manifest_url, meta }
  ThemeRegistry — Resource holding the entries; provides
    find(id), iter(), len(), is_empty().
  ThemeRegistryPlugin — Startup system that scans
    user_theme_dir() and populates the registry.
  build_registry(user_dir) — pure helper; takes the dir as a
    parameter so tests use tempfile::tempdir() without touching
    the global OnceLock-based user-theme path.
  refresh_registry(&mut, user_dir) — replaces in-place; called
    after a successful import_theme so a freshly-imported theme
    appears in the picker without an app restart.

The bundled default entry is always inserted (id "default", served
from DEFAULT_THEME_MANIFEST_URL) so the picker has at least one
option even when no user themes exist.

Discovery is best-effort: a directory whose theme.ron is missing,
malformed, or fails ThemeMeta::validate is silently skipped — broken
themes don't poison the registry. Only the meta block is parsed
(via a derive(Deserialize) struct that ignores other manifest
fields), which keeps startup quick even with dozens of themes
installed.

Wired into solitaire_app/main.rs after ThemePlugin so the asset
sources are registered before discovery scans for theme.ron files.

10 new tests covering: empty user dir, nonexistent user dir, valid
user theme registers, full-manifest tolerance via meta-only parser,
malformed theme.ron skipped, invalid-meta theme skipped, directory
without theme.ron ignored, find() returns None for unknown id,
refresh_registry replaces stale entries, default-entry URL matches
the embedded constant.

cargo build / clippy --workspace --all-targets -- -D warnings / test
--workspace all green (960 passed, 0 failed, 9 ignored).
2026-05-01 06:04:34 +00:00
funman300 7f477b4ad8 feat(engine): ThemePlugin + ActiveTheme integration (Card theme phase 4)
Implements Phase 4 of CARD_PLAN.md — the runtime hook that loads the
default theme on startup and refreshes the card-rendering pipeline
whenever the active theme changes.

solitaire_engine/src/theme/plugin.rs
  ThemePlugin
    init_asset::<CardTheme>, register_asset_loader for SvgLoader and
    CardThemeLoader, Startup load_default_theme, and Update
    sync_card_image_set_with_active_theme.
  ActiveTheme(Handle<CardTheme>)
    Resource pointing at the currently-loaded theme.
  set_theme(commands, asset_server, theme_id)
    Public API for switching themes — formats the URL as
    `themes://<theme_id>/theme.ron` and updates the resource.

Integration approach: rather than refactor every `card_plugin.rs`
spawn site to read from `Assets<CardTheme>` directly, the sync system
writes the theme's face/back image handles into the existing
`CardImageSet` resource on `AssetEvent::LoadedWithDependencies` /
`Modified`, then fires `StateChangedEvent`. The existing
`sync_cards_on_change` pipeline rebuilds card sprites from the new
handles on the next tick — observable behaviour matches the plan's
intent (theme switches propagate immediately) while keeping
card_plugin's 1929-line surface area untouched.

Theme.back is mapped onto `CardImageSet.backs[0]` (the default-back
slot xCards previously occupied); `backs[1..=4]` are the
asset-generator patterns and remain user-selectable independent of
the active theme.

Added to solitaire_app/main.rs as `add_plugins(ThemePlugin)` after
`AssetSourcesPlugin` so the asset sources are registered before the
default-theme load is dispatched.

6 new tests covering suit/rank index mapping (matching the
`card_plugin` doc-commented `[suit][rank]` layout), empty-theme
no-panic, back-slot overwrite, and the URL format from `set_theme`.

cargo build / clippy --workspace --all-targets -- -D warnings / test
--workspace all green (950 passed, 0 failed, 9 ignored).
2026-05-01 05:59:28 +00:00
funman300 ce38b26721 feat(engine): theme zip importer with safety validation (Card theme phase 7)
Implements Phase 7 of CARD_PLAN.md — the entry point that takes a
user-supplied theme zip archive, validates it end-to-end, and
atomically unpacks it into the per-platform user themes directory.

Public API:
  import_theme(zip_path) -> Result<ThemeId, ImportError>
    Resolves user_theme_dir() and unpacks into <user>/<id>/.
  import_theme_into(zip_path, target_root) -> Result<ThemeId, ImportError>
    Test-friendly variant that takes the destination explicitly so
    unit tests never touch the global OnceLock override.

Safety guarantees enforced:
- 20 MB hard cap on archive size (read from the central directory
  before any extraction).
- Zip-slip path traversal rejected via ZipFile::enclosed_name plus a
  Component::Normal-only belt-and-braces check.
- Manifest parsed via ron::de and validated via the existing
  ThemeManifest::validate (Phase 2) — surfaces named diagnostics for
  missing-of-52, unknown keys, duplicate keys, and meta errors.
- Every referenced face + back rasterised through rasterize_svg as a
  structural validity check before any bytes hit the destination.
- Atomic install: writes to <root>/.<id>.tmp/ then std::fs::rename
  into place, with a recursive copy + remove fallback for cross-
  device renames. Failed extraction wipes the staging dir; the user
  themes root is never touched on error.
- Id collision with an existing theme dir rejected up front.

7 new tests covering the happy path plus six failure modes (missing
manifest, missing face, oversized archive, zip-slip, missing-file,
id collision). Tests build zips in tempfile::TempDir so they never
touch the real user themes directory.

Workspace deps: zip 8.6 (default-features off + deflate only),
tempfile 3.27 (dev only).

cargo check --workspace --all-targets / clippy --workspace
--all-targets -- -D warnings clean. cargo test could not be run in
this turn because cc disappeared from the sandbox; tests compile
under cargo check --tests and will run on a normal toolchain.
2026-05-01 05:47:30 +00:00
funman300 172d7773f0 feat(engine): asset sources for embedded + user theme dirs (Card theme phase 3)
Implements Phase 3 of CARD_PLAN.md — the embedded:// + themes:// asset
sources the card-theme system loads from. The bundled default-theme
manifest ships in the binary via Bevy's EmbeddedAssetRegistry; user
themes load from user_theme_dir() through a FileAssetReader-backed
source registered as `themes://`.

Registration is split across:
  register_theme_asset_sources(&mut App)
    Called BEFORE DefaultPlugins. Registers `themes://` while
    AssetSourceBuilders is still mutable.
  AssetSourcesPlugin
    Added AFTER DefaultPlugins. Populates the EmbeddedAssetRegistry
    that AssetPlugin's build step would otherwise overwrite.

Constants exposed for downstream consumers:
  USER_THEMES                 = "themes"   (asset-source name)
  DEFAULT_THEME_MANIFEST_URL  = "embedded://solitaire_engine/assets/themes/default/theme.ron"

Includes a stub default theme.ron (52 face slots + back) so
`ThemeManifest::validate()` accepts it today; PROVENANCE.md documents
the plan to drop in real SVG art (hayeah/playing-cards-assets) in a
follow-up.

4 new tests covering source registration, embedded-registry
population, manifest validation against the embedded stub, and the
manifest-URL constant matching the embedded asset path.

cargo check --workspace --all-targets / clippy --workspace
--all-targets -- -D warnings clean. cargo test could not be run in
this turn because the C linker (cc) is unexpectedly absent from the
sandbox; the test bodies compile cleanly under cargo check --tests
and will run on a normal toolchain.
2026-05-01 05:47:13 +00:00
funman300 205ad6f646 feat(engine): per-platform user-theme directory (Card theme phase 5)
Implements Phase 5 of CARD_PLAN.md. Phase 3 (asset sources) and
Phase 7 (zip importer) both depend on this so it goes first.

solitaire_engine/src/assets/user_dir.rs
  user_theme_dir() -> PathBuf
    Desktop (Linux/macOS/Windows): joins dirs::data_dir() with
    "solitaire_quest/themes" — same parent as the rest of the
    project's per-user files (settings.json, stats.json, etc.)
    Mobile (Android/iOS): reads a process-wide OnceLock populated
    by set_user_theme_dir() at entry-point bootstrap. Panics with a
    targeted message if the override is missing — there is no
    platform default we can guess that won't be wrong inside iOS
    sandboxing or the Android storage model.
  set_user_theme_dir(PathBuf) -> Result<(), PathBuf>
    First-write-wins. Mobile entry points call this before App::run().

The plan suggested the `directories` crate; reused the existing `dirs`
workspace dep instead to keep the dependency surface minimal — both
crates share an author and the platform behaviour we need is identical.

3 new tests covering pure path composition (desktop nesting + empty
root) and a desktop-target-gated check that the detected data dir is
absolute. The OnceLock override is intentionally not unit-tested
because asserting its semantics would pollute global state for any
sibling test that calls `user_theme_dir()`.
2026-05-01 05:25:21 +00:00
funman300 936d035750 feat(engine): CardTheme asset + manifest loader (Card theme phase 2)
Implements Phase 2 of CARD_PLAN.md — the data types and `.theme.ron`
asset loader that build on Phase 1's SVG rasteriser.

solitaire_engine/src/theme/
  mod.rs        — CardKey { suit, rank } as the HashMap lookup key
                  (distinct from solitaire_core::Card which carries
                  per-deal id + face_up state); CardKey::all() yields
                  the 52 keys in suit-major / rank-ascending order;
                  manifest_name() and parse_manifest_name() round-trip
                  via the canonical "{suit}_{rank}" form.
                  ThemeMeta with structural validation (id non-empty,
                  no path separators, non-zero aspect components).
                  CardTheme #[derive(Asset, TypePath)] storing the
                  53 image handles + meta.
  manifest.rs   — ThemeManifest { meta, back, faces } with serde for
                  RON round-trip. validate() returns a strongly-typed
                  HashMap<CardKey, PathBuf>, surfacing precise errors
                  for unknown face keys, missing-of-52 entries, and
                  duplicate keys (RON silently keeps the last; brittle
                  for a release).
  loader.rs     — AssetLoader for .theme.ron. Validates manifest, then
                  composes sibling SVG paths via AssetPath::resolve so
                  the same loader works for both embedded:// and
                  themes:// asset sources (Phase 3 territory).
                  Schedules every face + back load through SvgLoader
                  with target_size derived from meta.card_aspect.

24 new tests covering: 52-key enumeration uniqueness, manifest-name
round trip, garbage-name rejection, complete/missing/unknown/duplicate
manifest validation, RON round-trip integrity, target-size aspect
math (2:3 → 512x768; non-standard; degenerate 1:10000 clamps to 1px).

Workspace deps added: ron 0.12.

cargo build / clippy --workspace --all-targets -- -D warnings / test
all green (937 passed total — +24 from Phase 2 vs the +7 from
Phase 1's b8fb3fb baseline).
2026-05-01 05:19:12 +00:00
funman300 13d1d013e9 chore: route rustc through sccache for cold-build wins
Adds .cargo/config.toml setting `rustc-wrapper = "sccache"` so cold
rebuilds (CI, fresh checkouts, post-`cargo clean`) replay previously-
compiled crates from disk instead of recompiling. Warm incremental
builds are unaffected — cargo's own target/ cache dominates there.

Cache lives at `.sccache-cache/` inside the project (gitignored). The
[env] entry uses `force = false` so a developer-set $SCCACHE_DIR in
their shell wins, matching whichever directory the sccache daemon
already adopted.

Requires sccache on PATH. Install: `pacman -S sccache`,
`brew install sccache`, or `cargo install sccache --locked`. Bypass
without editing: `RUSTC_WRAPPER= cargo build`.
2026-05-01 05:15:59 +00:00
funman300 b8fb3fbd6e feat(engine): SVG → Image asset loader (Card theme phase 1)
Implements the runtime SVG rasterisation pipeline that the card-theme
system (CARD_PLAN.md) is built on. Bevy 0.18 has no native SVG support;
this loader bridges usvg (parser) + resvg (renderer) + tiny-skia (CPU
pixmap) so the rest of the engine consumes themes as plain
Handle<Image>. Rasterisation happens once per (asset, settings) pair at
load time — Bevy's asset cache absorbs the cost.

solitaire_engine/src/assets/
  mod.rs           — module entrypoint
  svg_loader.rs    — SvgLoader (AssetLoader for .svg → Image)
                     SvgLoaderSettings { target_size: UVec2 } default 512×768
                     SvgLoaderError (Io / Parse / PixmapAlloc) via thiserror
                     rasterize_svg() helper exposed for non-asset-graph
                     callers (the future zip-importer validation step)

The rasteriser scales-to-fit while preserving aspect ratio, centring
the SVG inside the target box so a non-2:3 source doesn't pin to the
top-left corner.

7 new unit tests — default + custom target size, zero-dimension reject,
malformed-input reject, RGBA byte-count, extension advertisement, and
a compile-time guard that SvgLoaderSettings still satisfies the
AssetLoader::Settings trait bounds.

Workspace deps added: usvg 0.47, resvg 0.47, tiny-skia 0.12 (latest
minor versions; CARD_PLAN.md called out the placeholder numbers
needed verification).

cargo build / cargo clippy --workspace --all-targets -- -D warnings
/ cargo test --workspace all green (913 passed, 0 failed, 9 ignored —
+7 from the new loader tests).
2026-05-01 05:05:30 +00:00
funman300 e510e90b95 docs: refresh SESSION_HANDOFF for Phase 5 smoke-test fixes and bonus polish
CI / Test & Lint (push) Failing after 16s
CI / Release Build (push) Has been skipped
Phase 4 ended at 5d57b67 with the doc reflecting "16 commits ahead,
working tree dirty, splash always shows". The next session opened the
binary, found three real bugs (resize lag, off-screen cards,
mis-aligned hit-test), fixed them, and layered four bonus polish
items on top: persisted window geometry, achievement tooltips, a
clippy::pedantic sweep, client-side sync round-trip tests, and the
splash skip on subsequent launches.

Rewrites the doc to reflect the post-push state:
- HEAD: 902560c, branch up to date with origin
- 906 tests pass, working tree clean
- Phase 5 table summarising the nine new commits
- Stale "in-flight" / "deferred" items removed where they landed
- Punch list shrunk to xCards URL, v0.1.0 tag, desktop packaging

Length drops 142 → 121 lines (-21) by trimming the prior session's
"original Track G prompt" callbacks and recovery options that no
longer apply.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 04:22:39 +00:00
funman300 902560cd68 fix(engine): hit-test face-down fan offset matches sprite layout
CI / Test & Lint (push) Failing after 32s
CI / Release Build (push) Has been skipped
Smoke-test report: the user could only initiate a drag from the bottom
strip of a tableau card, not its visible face. Root cause was a fan-
step mismatch between rendering and hit-testing.

card_plugin::card_positions steps face-down cards by
TABLEAU_FACEDOWN_FAN_FRAC (0.12) and face-up cards by TABLEAU_FAN_FRAC
(0.25), so a column with 6 face-down + 1 face-up at the bottom
renders the face-up card at base.y - 0.72 * card_h. input_plugin's
card_position used a uniform 0.25 step for every position, computing
the same card's hit-test centre as base.y - 1.5 * card_h — almost a
full card height below the visible sprite. The hit-test AABB and the
sprite AABB overlapped only over the bottom 0.61 * card_h, which
matches the user's observation that only the bottom of the card
responds to clicks.

card_position now mirrors card_plugin's exact logic: walk the pile's
preceding cards and step by TABLEAU_FACEDOWN_FAN_FRAC for face-down,
TABLEAU_FAN_FRAC for face-up. TABLEAU_FACEDOWN_FAN_FRAC is now public
for the same reason TABLEAU_FAN_FRAC already was — the renderer and
the hit-tester have to agree by construction or this regression
returns.

Updates the existing find_draggable_skips_face_down_cards test that
relied on the old uniform-fan geometry, and adds
find_draggable_hits_face_up_card_with_face_down_cards_above_it as a
regression test that fails without this fix.

The during-drag rendering and pile_drop_rect still use the uniform
TABLEAU_FAN_FRAC because the cards being dragged are guaranteed
face-up, and a slightly oversized drop target reads as forgiving
rather than wrong. Those call sites are intentionally untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 03:33:46 +00:00
funman300 912b08c719 feat(engine): skip splash on subsequent launches
CI / Test & Lint (push) Failing after 34s
CI / Release Build (push) Has been skipped
The 1.6 s brand beat is delightful on first launch and tedious on
every subsequent one. spawn_splash now reads SettingsResource and
returns early when first_run_complete is true — the player has
already seen the splash at least once and the onboarding flow that
follows it, so dropping straight into gameplay is the right move.

Reuses the existing first_run_complete signal rather than introducing
a separate splash_seen field; the two states ("I've been here") line
up naturally and avoid carrying a one-shot flag forever.

The first run, a save reset (settings.json deleted), or a headless
test fixture that doesn't register SettingsResource all still see the
full splash — Option<Res<SettingsResource>> defaults to "show" when
absent, so the existing test fixture observes the same spawn it
always did.

Two new tests pin the split: splash_skipped_when_first_run_complete
asserts no SplashRoot spawns when settings say so, and
splash_still_shows_when_first_run_incomplete asserts the first-run
path is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 03:15:36 +00:00
funman300 3ef4ecb747 test(data): client-side sync round-trip integration tests
CI / Test & Lint (push) Failing after 6m45s
CI / Release Build (push) Has been skipped
Server-side endpoint tests already exist in solitaire_server. This
adds the client-side counterpart: five integration tests in
solitaire_data/tests/sync_round_trip.rs that drive
SolitaireServerClient against an in-process axum::serve harness with
an in-memory SQLite database, covering:

- register_login_push_pull_round_trip — happy path: register, push
  non-default stats, pull from a fresh client, assert the merged
  payload reflects the pushed values
- pull_after_concurrent_pushes_merges_correctly — two clients on one
  user push different games_played values, verify the server-side
  merge returns the max
- unauthenticated_pull_returns_authentication_error — pull without
  tokens surfaces SyncError::Auth as expected
- jwt_refresh_on_401_succeeds — replace the access token with one
  whose exp is two hours stale (same signing key), pull triggers
  401 → /api/auth/refresh → retry, asserts the call ultimately
  succeeds
- pull_after_account_deletion_returns_default_or_error — register,
  push, delete via the trait, confirm the next push surfaces a
  result rather than panicking

keyring_core's mock store is installed once per process via Once;
each test uses a unique username so the shared store doesn't
cross-contaminate. Production code in sync_client.rs needed no
changes — the Box<dyn SyncProvider> design plus the mock keyring
were sufficient to drive every flow from outside.

solitaire_server is added as a path dev-dependency along with the
direct crates the harness needs (axum, sqlx, jsonwebtoken, uuid,
chrono, solitaire_sync); no runtime deps changed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 02:46:48 +00:00
funman300 4b9d008be2 refactor(workspace): sweep low-risk clippy::pedantic findings
Conservative cleanup pass — applied only the high-signal pedantic
lints whose fixes either remove genuine waste or read more naturally,
skipping anything stylistic that would bloat the diff.

- map_unwrap_or: 29 .map(...).unwrap_or(...) sites collapsed to
  .map_or / .is_some_and / .map_or_else equivalents
- uninlined_format_args: 7 production format!/write!/println! sites
  rewritten to the inline-argument style; assert! sites in test code
  intentionally untouched
- match_same_arms: 2 redundant arms collapsed where the bodies were
  identical and the merger didn't obscure intent

Public API is unchanged. No dependencies added or removed. The
pedantic warning count dropped from 840 to 807 (-33). Out-of-scope
findings — needless_pass_by_value on Bevy Res params, false-positive
explicit_iter_loop on Bevy Query iterators, items_after_statements
inside test mods, and the "ask before changing" merge logic in
solitaire_sync — were intentionally deferred.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 02:46:32 +00:00
funman300 74482252d1 feat(engine): tooltips on Achievements screen rows
Each achievement row now carries a Tooltip whose text is derived from
the row's unlock state and the AchievementDef's reward, surfacing
information the row layout doesn't already show.

Four-state policy:
- Unlocked + reward → "Reward: <reward>." (e.g. "Reward: Card Back #1.")
- Unlocked + no reward → "Earned!"
- Locked, non-secret → "How to unlock: <description>." plus
  " Reward: <reward>." when one exists
- Locked, secret → no tooltip; the existing row-spawn skip preserves
  the achievement's discovery surprise

The row spawn loop tags each row with a new AchievementRow marker so
tests can locate them; the helper tooltip_for_row keeps the policy in
one place.

Six tests pin the policy: one full-flow test for unlocked + reward
mention, one secret-row negative test that asserts no tooltip
contains the verbatim secret condition or the secret reward, plus
four direct unit tests on tooltip_for_row covering all four states.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 02:18:04 +00:00
funman300 6e7705b256 feat(app): persist window geometry across launches
Settings gains an optional window_geometry field (size + position)
serialized via #[serde(default)] so legacy settings.json files without
the field deserialize cleanly to None. On launch the app restores
the persisted dimensions and position; first run and pre-upgrade
saves keep the existing 1280x800 centered default.

settings_plugin records changes from WindowResized and WindowMoved
into a PendingWindowGeometry resource and writes them to disk through
the existing atomic .tmp+rename path once the events have stayed
quiet for WINDOW_GEOMETRY_DEBOUNCE_SECS (0.5s). A merge_geometry
helper preserves whichever component (size or position) the latest
event burst didn't carry, so a position-only WindowMoved never wipes
the recorded size.

Pure should_persist_geometry and merge_geometry helpers are unit
tested for the boundary cases. Headless integration tests cover the
full flow: a single resize event then a quiet window persists, a
move event after a resize updates only position, a rapid storm
collapses to the final size, and a quiet frame with no events
leaves the geometry untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 02:17:54 +00:00
funman300 59316de1e9 perf(app): set PresentMode::AutoNoVsync to eliminate window-resize stalls
CI / Test & Lint (push) Failing after 18s
CI / Release Build (push) Has been skipped
Smoke-test report: window resize was still laggy after the card-side
throttle landed. Diagnosis pointed at the wgpu / OS layer rather than
ECS work — Bevy's default PresentMode is AutoVsync (Fifo), which gates
every frame on the monitor's vblank. On X11 / Wayland the compositor
sends WindowResized events at high frequency during a drag and the
vsync gate stalls each one, producing visible lag even when the
downstream systems do almost no work.

AutoNoVsync prefers Mailbox (triple-buffered, no blocking) and falls
back to Immediate when the backend can't honour Mailbox. Either is
fine for solitaire — the frame budget is tiny and the occasional
dropped frame from disabling vsync is imperceptible compared to the
stall this fixes.

Layered with the prior in-place resize updates and the 50ms
ResizeThrottle, this should bring the window-drag feel from "really
laggy" to native-feeling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 01:35:49 +00:00
funman300 1719fdada0 perf(engine): in-place resize updates and 50ms throttle eliminate drag lag
CI / Test & Lint (push) Failing after 24s
CI / Release Build (push) Has been skipped
Smoke-test report: dragging the window edge to resize was sluggish.
Profiling showed each WindowResized event triggered ~170 entity
mutations across all 52 cards: full Sprite regeneration via
card_sprite plus despawn_related on each card's CardLabel children
followed by a fresh with_children spawn — and WindowResized fires per
pixel of drag, multiplying the cost.

Three fixes layered together:

1. resize_cards_in_place is a new function the resize handler calls
   instead of sync_cards. It mutates Sprite.custom_size, the card's
   Transform.translation, and existing CardLabel TextFont.font_size
   directly — no Sprite replacement, no despawn_related, no child
   rebuild. update_card_entity stays unchanged for non-resize callers
   (deals, moves, flips, settings changes) so the full-repaint path
   they need is preserved.

2. collect_resize_events reads events.read().last() and stashes only
   the latest size into a ResizeThrottle resource each frame, so
   multiple WindowResized events in one frame collapse to one apply.

3. snap_cards_on_window_resize is gated by a 50ms throttle
   (RESIZE_THROTTLE_SECS): work runs at ~20 Hz during a sustained
   drag instead of ~120 Hz. When the user stops resizing the next
   frame flushes the final pending size, so the steady state always
   matches the released window dimensions. should_apply_resize is a
   pure helper unit-tested for the threshold-and-baseline contract.

apply_stock_empty_indicator gained a QueryFilter generic so the new
resize handler can pass a Without<CardEntity> filter — the resize
query already takes &mut Sprite on cards, so the indicator query had
to disjoin to avoid aliasing.

Five new tests pin the contract: should_apply_resize at three
threshold boundaries, plus integration tests that fire WindowResized
and assert no CardLabel entities were despawned and that
TextFont.font_size shrinks in place.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 01:06:14 +00:00
funman300 8dda9541a3 fix(engine): constrain card size so worst-case tableau fits vertically
The previous formula card_width = window.x / 9 with card_height = 1.4 *
card_width ignored the window height entirely. On a 1920×1080 window a
13-card face-up tableau column extended ~377 px below the viewport
bottom — visible reproduction in the smoke test.

compute_layout now derives two card_width candidates: one from the
horizontal grid budget (window.x / 9, unchanged) and one from the
vertical budget needed to seat 13 fanned cards plus the foundation
row, vertical_gap, and h_gap bottom margin. The smaller of the two
wins, so width remains the limiter on standard landscape windows and
height takes over on tall or short-wide aspect ratios. The math is
solved algebraically in a single substitution to avoid iteration.

When height is the limiter the original layout would have squished the
grid against the left edge; col_x now folds in a horizontal centring
offset that collapses to the existing geometry whenever width is the
limiter, so no other module needed an update.

Adds MAX_TABLEAU_CARDS = 13.0 (King-down-to-Ace worst case) and a
locally mirrored TABLEAU_FAN_FRAC = 0.25 — the original lives in
card_plugin and importing it would have created a circular dep with
layout. The duplication is doc-flagged so future drift gets noticed.

Four new tests pin both regimes: the height-limiter activates on a
1920×1080 window, stays inactive on a 900×1600 portrait window, and
the worst-case 13-card column fits on both 1280×800 and 1920×1080
within the bottom margin.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 01:05:57 +00:00
funman300 60a80369d4 docs: rewrite SESSION_HANDOFF for completed Phase 4 release-prep state
CI / Test & Lint (push) Failing after 21s
CI / Release Build (push) Has been skipped
The handoff document was written mid-overhaul during Phase 3 / early
Phase 4 and accumulated stale "in-flight" sections, recovery
instructions for a long-completed background agent, and a 7-track
speculative list of future directions that no longer matches reality.
This rewrite halves the document (284 → 142 lines) and reorients it
toward the actual current state: Phase 3 and Phase 4 shipped, every
substantial release-readiness thread landed, working tree clean.

Replaced the pause-state recovery section, original prompt blocks,
and Phase 3 smoke-test checklist with a compact table summarizing
what each Phase 4 commit landed, a chronological commit list as an
audit trail, and a five-item punch list scoping what's left for v1
(xCards URL, smoke test, push, tag v0.1.0, optional polish).

The resume prompt at the end is rewritten to orient a future agent
toward release prep — push, tag, package — rather than ongoing UX
work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 23:55:30 +00:00
funman300 dbe6c60133 feat(engine): tooltips on Modes and Menu popover rows
The earlier HUD tooltip pass deliberately skipped the popover row
content because the spawn helpers were inline and the popovers
ephemeral. Coming back to them now: every row in the Modes popover
(Classic / Daily Challenge / Zen / Challenge / Time Attack) and
every row in the Menu popover (Stats / Achievements / Profile /
Settings / Leaderboard) gets a one-sentence tooltip explaining what
opening that mode or screen does.

The row tuple in each popover spawn helper grew from
(Marker, label) to (Marker, label, tooltip), with Tooltip::new(...)
attached at the spawn site. No public helper signatures changed.

popover_rows_carry_tooltip_strings asserts every row's exact
canonical text by querying (With<ModeOption>, &Tooltip) and
(With<MenuOption>, &Tooltip), spawning the popovers directly via
world.commands() to keep the test independent of headless click
simulation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 23:55:20 +00:00
funman300 74597a8c84 feat(engine): tooltips on every Settings panel control
Eleven Settings controls — volume up/down for SFX and music, the four
toggle pills (draw mode, animation speed, theme, color-blind), the
two picker rows (card backs, backgrounds), and Sync Now — each gain a
one-sentence tooltip in the established Balatro voice. Static labels,
section headers, and live value readouts are intentionally skipped:
they are not interactive and the action button beside each describes
the action.

icon_button, volume_row, toggle_row, and picker_row gain
&'static str tooltip parameters so the tooltip is required at the
spawn site rather than retrofittable later. The Done button stays
tooltip-free (its label and Esc-equivalent affordance speak for
themselves at a modal-action position).

settings_buttons_carry_tooltip locks down the contract: every
SettingsButton outside the modal Done button has a Tooltip, and
SyncNow's tooltip text is asserted exactly to pin the canonical
microcopy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 23:55:10 +00:00
funman300 5d57b67934 feat(engine): branded splash screen on launch
The window previously snapped straight to a card deal, which read more
like a prototype than a finished game. SplashPlugin lays a fullscreen
overlay (BG_BASE backdrop, ACCENT_PRIMARY title, version subtitle) on
top of the gameplay layer for MOTION_SPLASH_TOTAL_SECS — the board
deals behind it so the splash dissolve hands off naturally to the
deal animation.

Visibility curves through fade-in (300ms), hold (~1s), fade-out
(300ms) using a pure splash_alpha helper that gets pinned by a unit
test rather than wired to the Bevy clock — Time<Virtual>'s 250ms
per-tick clamp makes float-tight alpha assertions around the fade
boundary brittle.

Any keystroke or mouse-button press jumps the age forward to the
fade-out window so the splash dissolves immediately. The dismiss
handler is read-only on ButtonInput / Touches, so the same press is
still visible to gameplay handlers downstream — pressing Space on the
splash both dismisses it and triggers the next-tick stock draw, as
verified by dismissal_keypress_is_visible_to_other_systems.

Z_SPLASH sits above every other UI rung (Z_TOAST + 100) so the splash
owns the viewport for its brief lifetime. The hierarchy test was
extended to enforce the new rung's monotonic position.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 23:31:13 +00:00
funman300 220e3f040c feat(engine): tooltips on every HUD readout and action button
Applies the tooltip infrastructure to the HUD: ten readouts (Score,
Moves, Time, Mode, daily-challenge target, draw cycle, undo count,
recycle count, auto-complete badge, keyboard selection chip) and the
six action-bar buttons (Menu, Undo, Pause, Help, Modes, New Game)
each gain a one-sentence tooltip in the established Balatro voice.

The strings earn their keep by surfacing information that isn't
visible: the link between the undo counter and the No Undo
achievement, the recycle counter and Comeback, the dual count-up /
countdown semantics of the timer in Time Attack, and the keyboard
shortcuts plus side-effects on action buttons.

spawn_action_button now requires a tooltip parameter so every action
bar entry gets one — there is no opt-out, by design. The popover Mode
and Menu rows are intentionally skipped: they're inside ephemeral
overlays whose hover surfaces are brief and already labeled.

Adds hud_elements_carry_expected_tooltip_strings, asserting the exact
text on each of the 16 instrumented elements.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 23:13:50 +00:00
funman300 54d34972d4 feat(engine): tooltip infrastructure with hover delay (foundation only)
A new ui_tooltip module owns a Tooltip(Cow<'static, str>) component
that turns any UI node into a hover-revealing help target. Bevy 0.18's
required-components attribute auto-inserts an Interaction so callers
just attach Tooltip and the rest is wired.

A single overlay entity is reparented above the focus ring (new
Z_TOOLTIP token = Z_FOCUS_RING + 10) and tracked from the hovered
target's GlobalTransform + ComputedNode. The chained Update systems
start a hover timer on Interaction::Hovered, show the overlay once
MOTION_TOOLTIP_DELAY_SECS (0.5s) has elapsed, hide it the moment hover
ends, and refresh the text when the hover target switches without an
intervening unhover.

Tested headless under MinimalPlugins with a 200ms ManualDuration
ticker — Bevy clamps Time<Virtual>'s max_delta to 250ms by default, so
a one-shot 1s step doesn't actually advance the clock past the
threshold; the tests step five times to exercise both pre- and
post-delay invariants.

This commit ships the infrastructure only — no entity in the engine
has Tooltip attached yet. A follow-up applies tooltips to the HUD
readouts and action bar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 23:03:43 +00:00
funman300 0c86cac2d5 feat(engine): unify destructive-confirm verbs — drop "Yes," prefix
Both confirm modals previously used a "Yes, <verb>" pattern that read
like a question-and-answer dialog ("Are you sure? Yes, forfeit"); the
canonical UX pattern for a destructive confirm is just the bare verb.

The Confirm New Game modal's primary button is now "New game" instead
of "Yes, abandon" — matching the verb the user originally clicked
and framing the action positively rather than as a loss.

The Forfeit Confirm modal's primary button is now "Forfeit" instead
of "Yes, forfeit" — same pattern, less ceremony.

The Pause menu's own Resume / Forfeit buttons are unchanged: it's an
action menu, not a destructive confirm, and bare verbs are already
correct there.

Two doc comments and the ui_modal.rs spawn_modal_button example
docstring are updated to reflect the new copy. Marker symbol names
(ConfirmYesButton, ForfeitConfirmButton) are kept to avoid
unnecessary churn — the rename would ripple into mouse-input handlers
without a matching user-visible benefit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 22:45:19 +00:00
funman300 2e080d02ce test(engine): integration coverage for draw_three_master and zen_winner
Closes the audit gap: the two achievements that previously had only
unit-level condition tests now also have full-flow tests that fire a
GameWonEvent and assert the unlock state through the same plugin
ordering production uses (update_stats_on_win runs before
evaluate_on_win, so the freshly bumped stat is visible to the
condition closure).

Four tests, headless under MinimalPlugins:
- draw_three_master_fires_on_tenth_draw_three_win — pre-seed 9 wins,
  fire a Draw3 win, assert unlock
- draw_three_master_does_not_fire_at_nine_wins — pre-seed 8, fire a
  Draw3 win bumping to 9, assert still locked
- zen_winner_fires_on_zen_mode_win — Zen-mode win unlocks the badge
- zen_winner_does_not_fire_for_classic_win — Classic win in same
  fixture leaves it locked

After this commit every advertised achievement has an integration
test that exercises the production unlock path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 22:28:32 +00:00
funman300 73e210b243 docs: replace bevy_kira_audio references with kira in ARCHITECTURE.md
§3, §5, and §13 all referenced bevy_kira_audio as the audio
dependency, but the workspace Cargo.toml has used kira 0.12 directly
since the kira-direct migration. Four mentions updated so the
architecture document matches the actual dependency graph.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 22:16:59 +00:00
funman300 f866299021 docs: drop xCards URL placeholder from CREDITS.md
The textual attribution to Huub de Beer / xCards / LGPL-3.0 already
satisfies the LGPL's notice requirement on its own; the unfilled
URL placeholder was the only TODO left in the file. Removed rather
than guessed — a confirmed upstream URL can be added in a follow-up
when the project owner decides which xCards mirror or fork to point
at.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 22:12:31 +00:00
funman300 b78a493a0c feat(engine): keyboard focus on Settings panel with arrow-key pickers (Phase 3)
Settings was the last mouse-only surface in the engine. Phase 3 closes
that gap and finishes the keyboard-focus rollout.

Every interactive button in the settings panel — icon buttons (32px
volume, draw mode, color blind, sync now), swatch pickers (5 card
backs, 5 backgrounds), and toggle pills — now opts into Focusable via
a single ancestry-walking system that mirrors the Phase 1/2 pattern.
The Done button continues to be auto-tagged through the modal path.

The two picker rows gain a new FocusRow marker. Inside a FocusRow,
Left/Right arrow keys cycle the swatches (skipping Disabled, wrapping
at endpoints) while Tab/Shift-Tab still escape to the next section's
focusable. Outside a FocusRow, arrow keys are explicit no-ops.

scroll_focus_into_view runs after the focus overlay updates and
adjusts the SettingsPanelScrollable container's ScrollPosition when
the focused button sits outside the visible viewport, with a
SPACE_2 padding so the focus ring never gets clipped at the
viewport edge. The system is a no-op when layout hasn't computed yet,
so headless tests are unaffected.

After Phase 3 every interactive UI element in the engine is
keyboard-navigable: modals (Phase 1), HUD action bar and Home mode
cards (Phase 2), Settings bespoke controls and picker rows (Phase 3).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 22:10:43 +00:00
funman300 51d3454344 feat(engine): keyboard focus on HUD action bar and Home mode cards (Phase 2)
The HUD action bar (Menu / Undo / Pause / Help / Modes / New Game) and
the five Home mode-launcher cards now participate in keyboard focus,
extending Phase 1's modal-only coverage.

The HUD focus group activates only when no modal is open and the
mouse is hovering an action-bar button — the design decision avoids
stealing Tab from selection_plugin's card-selection nav for the
common "playing on the board" case. Once engaged, Tab/Shift-Tab cycles
the bar in spawn order and Enter activates. Moving the mouse off the
bar clears focus so the ring doesn't linger.

Home mode cards opt into FocusGroup::Modal(home_scrim) via an
ancestry-walking system that mirrors the Phase 1 attach helper, so
spawn_mode_card's signature is unchanged. Locked cards (Zen,
Challenge, Time Attack at level <5) get the Disabled marker so Tab
skips them and Enter is a no-op — mirroring the existing visual
locked state with real keyboard semantics.

handle_focus_keys gains a Hud-on-hover branch in its active-group
resolver and a clear_hud_focus_on_unhover system. Together they
implement the agreed UX: focus follows hover when the bar is active,
Tab cycles within the hovered group, and the ring disappears the
instant the mouse leaves.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 21:41:31 +00:00
funman300 12789529a1 feat(engine): keyboard focus rings on modal buttons (Phase 1)
Every button spawned via spawn_modal_button is now keyboard-navigable.
Tab/Shift-Tab cycles focus within the active modal, Enter activates
the focused button via the same Interaction::Pressed signal mouse
clicks use, and the primary action auto-focuses on modal open. Mouse
clicks transfer focus so the two input modes stay in sync.

The visual indicator is a single overlay entity that's reparented
above the topmost modal scrim and tracks the focused button's
GlobalTransform + ComputedNode each frame. Sitting outside the
modal-card subtree means the ring isn't affected by the open
animation's 0.96→1.0 scale, and sitting outside any scroll container
means it can't be clipped by Settings' Overflow::scroll_y. Z-order
sits one rung above Z_MODAL_TOP via the new Z_FOCUS_RING token.

Existing 11 modals (Help, Stats, Achievements, Settings, Profile,
Leaderboard, Pause, Forfeit confirm, GameOver, Confirm new game,
Onboarding, Home) get focus support without any call-site changes —
attach_focusable_to_modal_buttons walks the ancestry of any
ModalButton lacking Focusable to find its scrim and tags it
automatically. selection_plugin's Tab handler keeps working when no
modal is open; when one is, focus consumes Tab/Enter before the
selection system sees them.

Phase 1 scope only — HUD action bar, Home mode cards, and Settings
bespoke buttons (icon, swatch, toggle) come in Phase 2/3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 21:17:25 +00:00
funman300 c1bde18a2c feat(engine): repurpose Home as mode launcher
The Home modal was previously a keyboard-shortcut reference card that
mostly duplicated Help. It now opens directly into a Mode Launcher:
five mode cards (Classic, Daily Challenge, Zen, Challenge, Time
Attack) stacked vertically with a Cancel button at the bottom.

Each card dispatches the canonical request event already used by the
HUD modes-popover (NewGameRequestEvent, StartDailyChallengeRequestEvent,
StartZenRequestEvent, StartChallengeRequestEvent,
StartTimeAttackRequestEvent), so level gates, daily-seed lookup, and
session setup all flow through the existing handlers — Home is just
another entry point.

The three modes that unlock at level 5 (Zen, Challenge, Time Attack)
render with reduced opacity and a "Reach level 5 to unlock" caption
when locked; clicking a locked card is a deliberate no-op so the
player can pick a different mode without dismissing the modal.

The keyboard-shortcut reference is dropped entirely — Help (F1) still
covers it. M continues to toggle the modal open and closed.

Adds 5 new headless tests covering card spawn, locked-state click,
unlocked-state click, Classic launch + close, and Cancel close.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 20:39:26 +00:00
funman300 fd7fb7b6da docs: add CREDITS.md and link from README
Lists the project's third-party assets and major dependencies with
their licenses for v1 release readiness:
- xCards @2x card artwork and back_0 (LGPL-3.0)
- FiraMono-Medium font (OFL)
- Bevy 0.18, kira 0.12, axum, sqlx, tokio, and the rest of the
  ten most prominent Rust deps (MIT/Apache-2.0 across the board)

Generated assets — card backs 1-4, all backgrounds, and every WAV
file — are credited as original work produced by the project's own
solitaire_assetgen pipeline; the audio synthesis stack and the
absence of any external sample sources are documented.

The README gains a brief Credits section linking to the full list.

Note: the upstream xCards source URL is left as a placeholder pending
confirmation; downstream license obligations are unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 20:36:42 +00:00
funman300 138436558f feat(engine): leaderboard error and idle states plus local-only guard
LeaderboardResource was a tuple struct of Option<Vec<Entry>>: None for
pre-fetch and empty Vec for both "actually empty" and "fetch failed"
— the user couldn't tell a network error from a legitimately quiet
leaderboard. The resource is now a four-state enum (Idle / Error /
Loaded), with Loaded covering both populated and empty rows. A
transient error no longer wipes a previously populated list, and the
panel renders "Couldn't reach the leaderboard. Try again later."
when the most recent fetch failed.

The Opt In / Opt Out buttons used to render unconditionally and
silently no-op under LocalOnlyProvider. The panel now reads the
SyncProviderResource backend name and, when no remote is configured,
replaces the buttons with a single line directing the player to
configure cloud sync in Settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 20:18:34 +00:00
funman300 65d595ad12 feat(engine): first-launch polish — em-dash zero stats and welcome line on profile
On first launch the Stats grid previously mixed "0" cells (Games
Played / Won / Lost) with "—" cells (Best Score / Win Rate / Avg
Time), reading as inconsistent. Now every cell renders an em-dash
when games_played == 0, and a "Play a game to start tracking stats."
caption sits above the grid using the existing TYPE_CAPTION /
TEXT_SECONDARY tokens. Once a game has been played the original
formatters resume.

The Profile screen gains a one-line welcome ("Welcome! Play games to
earn XP and unlock achievements.") that renders only when both
total_xp and the daily streak are zero, breaking up the wall of
zero-valued readouts that greeted users on first launch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 20:18:14 +00:00
funman300 abeb4e5cdf feat(engine): unify dismiss verb to Done and warm onboarding CTA to Let's play
The Help modal previously used "Close" while the other five overlay
modals (Home, Stats, Achievements, Settings, Profile, Leaderboard)
used "Done"; standardising on "Done" removes the outlier.

The final onboarding slide changes from "Start playing" to
"Let's play". The microcopy audit suggested matching the win modal's
"Play Again", but that verb is semantically wrong on first launch —
the player has not yet played. "Let's play" reads warmer and matches
the project's Balatro-tone direction without overloading "Play Again"
across two contexts that mean different things.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 20:18:02 +00:00
funman300 b082bd65a6 feat(engine): bump icon-button hit target to 32px and clarify local-only sync status
ICON_BUTTON_PX moves from 28 to 32 to clear the desktop hit-target
threshold. The change is self-contained: icon buttons are centered in
flex rows whose neighbours retain their alignment, and the swatch
buttons (40px) still dominate the visual hierarchy.

The settings sync status fallback string changes from "Status: not
configured" to "Status: local only" so users running without a remote
backend read it as a deliberate choice rather than incomplete setup.
The other status strings (Idle / Syncing / LastSynced / Error) flow
from sync_status_label and are unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 20:17:22 +00:00
170 changed files with 26415 additions and 854 deletions
+31
View File
@@ -0,0 +1,31 @@
# Project-wide cargo configuration.
#
# Routes every rustc invocation through `sccache` so cold rebuilds and
# fresh checkouts (CI, new dev box, after a `cargo clean`) replay
# previously-compiled crates from a local on-disk cache rather than
# recompiling them. Warm incremental builds still go through cargo's
# own `target/` cache, which dominates locally — sccache buys you the
# big wins on cold paths.
#
# Requires sccache on PATH. Install it once per machine:
#
# Arch : pacman -S sccache
# macOS : brew install sccache
# Cargo : cargo install sccache --locked
#
# Without sccache the build fails with "rustc-wrapper not found". To
# bypass this config without editing the file, prepend
# `RUSTC_WRAPPER= ` (empty value) to your cargo command:
#
# RUSTC_WRAPPER= cargo build
#
[build]
rustc-wrapper = "sccache"
# Project-local cache so the shared dev box (or a Docker volume) keeps
# the artefacts isolated per checkout instead of mixing them in
# `~/.cache/sccache`. Set with `force = false` so a developer-set
# `SCCACHE_DIR` in their shell wins — important because the sccache
# daemon, once started, sticks with whichever directory it saw first.
[env]
SCCACHE_DIR = { value = ".sccache-cache", relative = true, force = false }
+1
View File
@@ -1,4 +1,5 @@
/target /target
/.sccache-cache
*.db *.db
*.db-shm *.db-shm
*.db-wal *.db-wal
+9 -7
View File
@@ -70,8 +70,8 @@ solitaire_quest/
├── assets/ # Loaded at runtime via AssetServer (audio is embedded via include_bytes!()) ├── assets/ # Loaded at runtime via AssetServer (audio is embedded via include_bytes!())
│ ├── cards/ │ ├── cards/
│ │ ├── faces/{RANK}{SUIT}.png # 52 card faces — xCards @2x artwork (LGPL-3.0) │ │ ├── faces/{RANK}{SUIT}.png # 52 card faces — rendered from hayeah/playing-cards-assets SVGs (MIT)
│ │ └── backs/back_0.png back_4.png # back_0 = xCards bicycle_blue; back_14 are generated patterns │ │ └── backs/back_0.png back_4.png # back_0 = generated default back; back_14 are generated patterns
│ ├── backgrounds/bg_0.png bg_4.png # generated textures │ ├── backgrounds/bg_0.png bg_4.png # generated textures
│ ├── fonts/main.ttf # FiraMono-Medium (170K, OFL) │ ├── fonts/main.ttf # FiraMono-Medium (170K, OFL)
│ └── audio/ │ └── audio/
@@ -133,7 +133,7 @@ Owns:
- `SyncProvider` trait — implemented by `SolitaireServerClient` - `SyncProvider` trait — implemented by `SolitaireServerClient`
### `solitaire_engine` ### `solitaire_engine`
**Dependencies:** `bevy`, `bevy_kira_audio`, `solitaire_core`, `solitaire_data`. **Dependencies:** `bevy`, `kira`, `solitaire_core`, `solitaire_data`.
All Bevy-specific code. Structured as a collection of Plugins that `solitaire_app` registers. All Bevy-specific code. Structured as a collection of Plugins that `solitaire_app` registers.
@@ -246,7 +246,7 @@ The "Shortcut" column lists optional keyboard accelerators. Every action in this
| `AnimationPlugin` | — | Slide, flip, win cascade, toast animations | | `AnimationPlugin` | — | Slide, flip, win cascade, toast animations |
| `FeedbackAnimPlugin` | — | Shake, settle, and deal-stagger animations | | `FeedbackAnimPlugin` | — | Shake, settle, and deal-stagger animations |
| `AutoCompletePlugin` | Enter | Executes auto-complete when the HUD badge is lit | | `AutoCompletePlugin` | Enter | Executes auto-complete when the HUD badge is lit |
| `AudioPlugin` | — | Sound effect and music playback via bevy_kira_audio | | `AudioPlugin` | — | Sound effect and music playback via kira |
| `InputPlugin` | — | Keyboard and mouse input routing | | `InputPlugin` | — | Keyboard and mouse input routing |
| `CursorPlugin` | — | Custom cursor sprite during drag | | `CursorPlugin` | — | Custom cursor sprite during drag |
| `SelectionPlugin` | — | Keyboard-driven card selection | | `SelectionPlugin` | — | Keyboard-driven card selection |
@@ -754,7 +754,7 @@ Levels 11+: level = 10 + floor((total_xp - 5000) / 1000)
## 13. Audio System ## 13. Audio System
Audio uses `bevy_kira_audio`. All sound files are `.wav`. Audio uses `kira`. All sound files are `.wav`.
| File | Trigger | | File | Trigger |
|---|---| |---|---|
@@ -765,7 +765,7 @@ Audio uses `bevy_kira_audio`. All sound files are `.wav`.
| `win_fanfare.wav` | Game won | | `win_fanfare.wav` | Game won |
| `ambient_loop.wav` | Looping background music | | `ambient_loop.wav` | Looping background music |
Volume is controlled by two independent sliders in Settings (`sfx_volume`, `music_volume`), each stored in `Settings` and applied as `bevy_kira_audio` channel volumes. Volume is controlled by two independent sliders in Settings (`sfx_volume`, `music_volume`), each stored in `Settings` and applied as `kira` channel volumes.
Audio systems listen for Bevy events and never block the game thread. Audio systems listen for Bevy events and never block the game thread.
@@ -1009,5 +1009,7 @@ Using `axum::test` and an in-memory SQLite database:
| `SyncProvider` trait, not `SyncBackend` match arms | `SyncPlugin` stays backend-agnostic and testable; new backends can be added without touching the plugin | 2026-04-20 | | `SyncProvider` trait, not `SyncBackend` match arms | `SyncPlugin` stays backend-agnostic and testable; new backends can be added without touching the plugin | 2026-04-20 |
| Dropped WebDAV backend | Redundant once the self-hosted server exists; removing it reduces surface area and simplifies settings UI | 2026-04-20 | | Dropped WebDAV backend | Redundant once the self-hosted server exists; removing it reduces surface area and simplifies settings UI | 2026-04-20 |
| Dropped GPGS backend | Redundant with the self-hosted server; adds JNI complexity for no user-visible benefit on the target platforms | 2026-04-28 | | Dropped GPGS backend | Redundant with the self-hosted server; adds JNI complexity for no user-visible benefit on the target platforms | 2026-04-28 |
| Card, background, and font assets loaded via `AssetServer` | Reverses the earlier embed-via-`include_bytes!()` decision: PNGs and TTFs are loaded at runtime so artwork can be swapped (e.g. xCards @2x faces, alternate card backs, themed backgrounds) without a recompile, and binary size stays small. Loaders take `Option<Res<AssetServer>>` and fall back gracefully under `MinimalPlugins`. The `assets/` directory must ship alongside the binary. | 2026-04-29 | | Card, background, and font assets loaded via `AssetServer` | Reverses the earlier embed-via-`include_bytes!()` decision: PNGs and TTFs are loaded at runtime so artwork can be swapped (e.g. alternate card backs, themed backgrounds) without a recompile, and binary size stays small. Loaders take `Option<Res<AssetServer>>` and fall back gracefully under `MinimalPlugins`. The `assets/` directory must ship alongside the binary. | 2026-04-29 |
| Audio assets remain embedded via `include_bytes!()` | Audio files are small, change rarely, and the embedded path eliminates a class of runtime-load errors during gameplay; the asset-pipeline reversal does not extend to audio | 2026-04-29 | | Audio assets remain embedded via `include_bytes!()` | Audio files are small, change rarely, and the embedded path eliminates a class of runtime-load errors during gameplay; the asset-pipeline reversal does not extend to audio | 2026-04-29 |
| Card art swapped from xCards (LGPL-3.0) to hayeah/playing-cards-assets (MIT) | Public-release readiness. The previous xCards art carried LGPL relinking obligations that complicate a single-binary distribution; hayeah's set derives from the public-domain `vector-playing-cards` line-art and is permissively MIT-licensed. CREDITS.md license summary collapsed to MIT + OFL-1.1. The default card back is original work in this project's midnight-purple palette. | 2026-05-01 |
| Runtime SVG card-theme system (`CARD_PLAN.md`) | User-supplied themes need to ship SVG sources so they can rasterise at any resolution on the player's hardware; baking PNGs at build time only would lock theme installation to the developer. The pipeline (usvg → resvg → tiny-skia) rasterises once per (theme, target size) at load time and caches the resulting `Image`, so the runtime cost is paid once, not per frame. The bundled default theme ships via `embedded://`; user themes via `themes://` rooted at `user_theme_dir()`. | 2026-05-01 |
+112
View File
@@ -0,0 +1,112 @@
# Credits
Solitaire Quest is MIT-licensed (see [LICENSE](LICENSE)). It is built on top of
the work of many open-source projects and a small handful of third-party
assets. This file lists every component that ships in the binary or in the
`assets/` directory.
---
## Code & Framework
| Component | License | Role |
|---|---|---|
| [Bevy 0.18](https://bevyengine.org/) | MIT OR Apache-2.0 | Game engine, ECS, rendering, UI |
| [kira 0.12](https://crates.io/crates/kira) | MIT OR Apache-2.0 | Audio playback (mixer, sub-tracks, looping ambient) |
| [serde](https://crates.io/crates/serde) / [serde_json](https://crates.io/crates/serde_json) | MIT OR Apache-2.0 | Serialization for save files and the sync API |
| [tokio](https://crates.io/crates/tokio) | MIT | Async runtime for the sync client and server |
| [axum 0.8](https://crates.io/crates/axum) | MIT | HTTP framework for the self-hosted sync server |
| [sqlx 0.8](https://crates.io/crates/sqlx) | MIT OR Apache-2.0 | Compile-time-checked SQLite access on the server |
| [reqwest 0.13](https://crates.io/crates/reqwest) | MIT OR Apache-2.0 | HTTP client for the sync provider |
| [jsonwebtoken 10](https://crates.io/crates/jsonwebtoken) | MIT | JWT issuance and validation |
| [bcrypt 0.19](https://crates.io/crates/bcrypt) | MIT | Password hashing on the server |
| [keyring 4](https://crates.io/crates/keyring) | MIT OR Apache-2.0 | OS keychain integration for credential storage |
| [tower-governor 0.8](https://crates.io/crates/tower-governor) | MIT | Rate limiting on `/api/auth/*` |
| [chrono](https://crates.io/crates/chrono) | MIT OR Apache-2.0 | Date / time handling |
| [uuid](https://crates.io/crates/uuid) | MIT OR Apache-2.0 | User and session identifiers |
| [thiserror](https://crates.io/crates/thiserror) | MIT OR Apache-2.0 | Error type derive |
| [rand 0.9](https://crates.io/crates/rand) | MIT OR Apache-2.0 | Seeded shuffler in `solitaire_core` |
| [png 0.17](https://crates.io/crates/png) | MIT OR Apache-2.0 | PNG encoder used by `solitaire_assetgen` |
| [ab_glyph 0.2](https://crates.io/crates/ab_glyph) | Apache-2.0 | Glyph rasterization for generated card art |
The full transitive dependency tree (several hundred crates) is captured in
`Cargo.lock` and reachable via `cargo tree`. Every crate brought in is
MIT, Apache-2.0, BSD-style, or a dual-licensed combination thereof — no
copyleft code is statically linked into the game binary.
---
## Assets
### Card artwork
| File(s) | Source | License |
|---|---|---|
| `solitaire_engine/assets/themes/default/{suit}_{rank}.svg` (52 SVGs) | [hayeah/playing-cards-assets](https://github.com/hayeah/playing-cards-assets) | MIT |
| `solitaire_engine/assets/themes/default/back.svg` | Original — Solitaire Quest | MIT (this project) |
| `assets/cards/faces/{RANK}{SUIT}.png` (52 PNGs) | Pre-rendered from the same `playing-cards-assets` SVGs | MIT (passed through from hayeah) |
| `assets/cards/backs/back_0.png` `back_4.png` | Original — generated by `solitaire_assetgen::gen_art` | MIT (this project) |
The face SVGs come from Howard Yeh's `playing-cards-assets` repository, which
is itself derived from the public-domain `vector-playing-cards` Google Code
project. The art is redistributed under the MIT license — see the upstream
repository for the full notice. The files ship unmodified in the bundled
default theme; user-supplied themes can override them per-installation
through the runtime SVG theming system documented in `CARD_PLAN.md`.
The default card back is original work by this project, midnight-purple
themed to match the rest of the UI palette.
### Backgrounds
| File(s) | Source | License |
|---|---|---|
| `assets/backgrounds/bg_0.png` `bg_4.png` | Original — generated by `solitaire_assetgen::gen_art` | MIT (this project) |
### Typography
| File | Source | License |
|---|---|---|
| `assets/fonts/main.ttf` (FiraMono-Medium) | [mozilla/Fira](https://github.com/mozilla/Fira) | SIL Open Font License 1.1 |
The OFL permits redistribution and embedding in software so long as the font
file itself is not sold standalone. The file ships unmodified.
### Audio
All six WAV files in `assets/audio/` are **original work** — there are no
third-party audio samples in this project. They are synthesized
programmatically by `solitaire_assetgen/src/bin/gen_sfx.rs`, which writes
44.1 kHz mono 16-bit PCM WAVs using a hand-rolled WAV writer (no `hound` or
`dasp` dependency). The synthesis stack is entirely additive: sine /
square waves, layered harmonics, deterministic LCG noise, AR envelopes,
and a slow LFO for the ambient track.
| File | Synthesis approach |
|---|---|
| `card_deal.wav` | Filtered LCG noise with a sweeping low-pass cutoff for a "whoosh" |
| `card_flip.wav` | High-passed LCG noise under a fast AR envelope |
| `card_place.wav` | 120 Hz sine body + filtered noise click |
| `card_invalid.wav` | Two dissonant square tones (196 Hz + 207.65 Hz) beating against each other |
| `win_fanfare.wav` | C-major arpeggio (C5 / E5 / G5 / C6) with sine + 2nd harmonic |
| `ambient_loop.wav` | 55 Hz fundamental with 2nd and 3rd harmonics, modulated by a 0.2 Hz LFO; loop length is chosen so the tone and LFO both complete an integer number of cycles for seamless looping |
Audio files are MIT-licensed alongside the rest of this project.
---
## License Summary
- **Project code:** MIT — see [LICENSE](LICENSE).
- **Card face artwork (52 SVGs from hayeah/playing-cards-assets, plus the
pre-rendered PNGs in `assets/cards/faces/`):** MIT, redistributed
unmodified. The original `vector-playing-cards` line art is itself
public domain.
- **FiraMono-Medium font:** SIL Open Font License 1.1, redistributed unmodified.
- **All other assets** (backgrounds, the default `back.svg`, generated card
backs, every audio file) are original work covered by this project's MIT
license.
If you redistribute Solitaire Quest, you must ship this `CREDITS.md` and the
`LICENSE` file alongside the binary so the MIT (project + hayeah card art)
and OFL (FiraMono) notices remain visible to end users.
Generated
+299 -16
View File
@@ -2429,6 +2429,12 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.5" version = "1.0.5"
@@ -2950,6 +2956,12 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
name = "data-url"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
[[package]] [[package]]
name = "datasketches" name = "datasketches"
version = "0.2.0" version = "0.2.0"
@@ -3477,8 +3489,15 @@ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide",
"zlib-rs",
] ]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.11.1" version = "0.11.1"
@@ -3532,7 +3551,7 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646"
dependencies = [ dependencies = [
"roxmltree", "roxmltree 0.20.0",
] ]
[[package]] [[package]]
@@ -3851,6 +3870,16 @@ dependencies = [
"polyval", "polyval",
] ]
[[package]]
name = "gif"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159"
dependencies = [
"color_quant",
"weezl",
]
[[package]] [[package]]
name = "gilrs" name = "gilrs"
version = "0.11.1" version = "0.11.1"
@@ -4529,6 +4558,22 @@ dependencies = [
"png 0.18.1", "png 0.18.1",
] ]
[[package]]
name = "image-webp"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
dependencies = [
"byteorder-lite",
"quick-error",
]
[[package]]
name = "imagesize"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.14.0" version = "2.14.0"
@@ -4856,6 +4901,17 @@ dependencies = [
"bitflags 2.11.1", "bitflags 2.11.1",
] ]
[[package]]
name = "kurbo"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7564e90fe3c0d5771e1f0bc95322b21baaeaa0d9213fa6a0b61c99f8b17b3bfb"
dependencies = [
"arrayvec",
"euclid",
"smallvec",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.5.0" version = "1.5.0"
@@ -6011,15 +6067,6 @@ dependencies = [
"libredox", "libredox",
] ]
[[package]]
name = "ordered-float"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "ordered-float" name = "ordered-float"
version = "5.3.0" version = "5.3.0"
@@ -6165,6 +6212,12 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.11" version = "1.1.11"
@@ -6428,6 +6481,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.39.2" version = "0.39.2"
@@ -6798,6 +6857,23 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "resvg"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be183ad6a216aa96f33e4c8033b0988b8b3ea6fd2359d19af5bac4643fd8e81"
dependencies = [
"gif",
"image-webp",
"log",
"pico-args",
"rgb",
"svgtypes",
"tiny-skia 0.12.0",
"usvg",
"zune-jpeg",
]
[[package]] [[package]]
name = "rfc6979" name = "rfc6979"
version = "0.4.0" version = "0.4.0"
@@ -6808,6 +6884,15 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "rgb"
version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.14" version = "0.17.14"
@@ -6862,6 +6947,15 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]]
name = "roxmltree"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "rpassword" name = "rpassword"
version = "7.5.0" version = "7.5.0"
@@ -7068,6 +7162,24 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "rustybuzz"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702"
dependencies = [
"bitflags 2.11.1",
"bytemuck",
"core_maths",
"log",
"smallvec",
"ttf-parser",
"unicode-bidi-mirroring",
"unicode-ccc",
"unicode-properties",
"unicode-script",
]
[[package]] [[package]]
name = "ruzstd" name = "ruzstd"
version = "0.8.2" version = "0.8.2"
@@ -7123,7 +7235,7 @@ dependencies = [
"log", "log",
"memmap2", "memmap2",
"smithay-client-toolkit", "smithay-client-toolkit",
"tiny-skia", "tiny-skia 0.11.4",
] ]
[[package]] [[package]]
@@ -7382,6 +7494,15 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "simplecss"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
dependencies = [
"log",
]
[[package]] [[package]]
name = "simsimd" name = "simsimd"
version = "6.5.16" version = "6.5.16"
@@ -7522,7 +7643,6 @@ dependencies = [
name = "solitaire_core" name = "solitaire_core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono",
"rand 0.9.4", "rand 0.9.4",
"serde", "serde",
"thiserror 2.0.18", "thiserror 2.0.18",
@@ -7533,16 +7653,21 @@ name = "solitaire_data"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum",
"chrono", "chrono",
"dirs", "dirs",
"jsonwebtoken",
"keyring-core", "keyring-core",
"reqwest", "reqwest",
"serde", "serde",
"serde_json", "serde_json",
"solitaire_core", "solitaire_core",
"solitaire_server",
"solitaire_sync", "solitaire_sync",
"sqlx",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"uuid",
] ]
[[package]] [[package]]
@@ -7552,12 +7677,21 @@ dependencies = [
"async-trait", "async-trait",
"bevy", "bevy",
"chrono", "chrono",
"dirs",
"kira", "kira",
"resvg",
"ron",
"serde",
"solitaire_core", "solitaire_core",
"solitaire_data", "solitaire_data",
"solitaire_sync", "solitaire_sync",
"tempfile",
"thiserror 2.0.18",
"tiny-skia 0.12.0",
"tokio", "tokio",
"usvg",
"uuid", "uuid",
"zip",
] ]
[[package]] [[package]]
@@ -7588,7 +7722,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"serde", "serde",
"serde_json",
"thiserror 2.0.18", "thiserror 2.0.18",
"uuid", "uuid",
] ]
@@ -7855,6 +7988,9 @@ name = "strict-num"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]] [[package]]
name = "stringprep" name = "stringprep"
@@ -7907,6 +8043,16 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb"
[[package]]
name = "svgtypes"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d"
dependencies = [
"kurbo",
"siphasher",
]
[[package]] [[package]]
name = "swash" name = "swash"
version = "0.2.7" version = "0.2.7"
@@ -8221,7 +8367,7 @@ checksum = "dfadb8526b6da90704feb293b0701a6aae62ea14983143344be2dc5ce30f1d82"
dependencies = [ dependencies = [
"fnv", "fnv",
"nom", "nom",
"ordered-float 5.3.0", "ordered-float",
"serde", "serde",
"serde_json", "serde_json",
] ]
@@ -8378,7 +8524,22 @@ dependencies = [
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",
"log", "log",
"tiny-skia-path", "tiny-skia-path 0.11.4",
]
[[package]]
name = "tiny-skia"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47ffee5eaaf5527f630fb0e356b90ebdec84d5d18d937c5e440350f88c5a91ea"
dependencies = [
"arrayref",
"arrayvec",
"bytemuck",
"cfg-if",
"log",
"png 0.18.1",
"tiny-skia-path 0.12.0",
] ]
[[package]] [[package]]
@@ -8392,6 +8553,17 @@ dependencies = [
"strict-num", "strict-num",
] ]
[[package]]
name = "tiny-skia-path"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edca365c3faccca67d06593c5980fa6c57687de727a03131735bb85f01fdeeb9"
dependencies = [
"arrayref",
"bytemuck",
"strict-num",
]
[[package]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.8.3" version = "0.8.3"
@@ -8942,6 +9114,12 @@ dependencies = [
"rand 0.9.4", "rand 0.9.4",
] ]
[[package]]
name = "typed-path"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
[[package]] [[package]]
name = "typeid" name = "typeid"
version = "1.0.3" version = "1.0.3"
@@ -9010,6 +9188,18 @@ version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]]
name = "unicode-bidi-mirroring"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe"
[[package]]
name = "unicode-ccc"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.24" version = "1.0.24"
@@ -9049,6 +9239,12 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
[[package]]
name = "unicode-vo"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.14" version = "0.1.14"
@@ -9089,6 +9285,34 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "usvg"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d46cf96c5f498d36b7a9693bc6a7075c0bb9303189d61b2249b0dc3d309c07de"
dependencies = [
"base64",
"data-url",
"flate2",
"fontdb",
"imagesize",
"kurbo",
"log",
"pico-args",
"roxmltree 0.21.1",
"rustybuzz",
"simplecss",
"siphasher",
"strict-num",
"svgtypes",
"tiny-skia-path 0.12.0",
"ttf-parser",
"unicode-bidi",
"unicode-script",
"unicode-vo",
"xmlwriter",
]
[[package]] [[package]]
name = "utf8-ranges" name = "utf8-ranges"
version = "1.0.5" version = "1.0.5"
@@ -9448,6 +9672,12 @@ dependencies = [
"rustls-pki-types", "rustls-pki-types",
] ]
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]] [[package]]
name = "wgpu" name = "wgpu"
version = "27.0.1" version = "27.0.1"
@@ -9566,7 +9796,7 @@ dependencies = [
"ndk-sys 0.6.0+11769913", "ndk-sys 0.6.0+11769913",
"objc", "objc",
"once_cell", "once_cell",
"ordered-float 4.6.0", "ordered-float",
"parking_lot", "parking_lot",
"portable-atomic", "portable-atomic",
"portable-atomic-util", "portable-atomic-util",
@@ -10495,6 +10725,12 @@ version = "0.8.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]] [[package]]
name = "yazi" name = "yazi"
version = "0.2.1" version = "0.2.1"
@@ -10696,12 +10932,44 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "zip"
version = "8.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b"
dependencies = [
"crc32fast",
"flate2",
"indexmap",
"memchr",
"typed-path",
"zopfli",
]
[[package]]
name = "zlib-rs"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513"
[[package]] [[package]]
name = "zmij" name = "zmij"
version = "1.0.21" version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]]
name = "zopfli"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
dependencies = [
"bumpalo",
"crc32fast",
"log",
"simd-adler32",
]
[[package]] [[package]]
name = "zstd" name = "zstd"
version = "0.13.3" version = "0.13.3"
@@ -10730,6 +10998,21 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "zune-core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
[[package]]
name = "zune-jpeg"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [
"zune-core",
]
[[package]] [[package]]
name = "zvariant" name = "zvariant"
version = "5.10.1" version = "5.10.1"
+24
View File
@@ -38,6 +38,30 @@ solitaire_engine = { path = "solitaire_engine" }
bevy = "0.18" bevy = "0.18"
kira = "0.12" kira = "0.12"
# SVG rasterisation pipeline for the runtime card-theme system.
# usvg parses + simplifies; resvg renders to a tiny-skia Pixmap;
# tiny-skia provides the CPU rasteriser. All three are maintained
# together by the resvg-rs project and version in lockstep.
usvg = "0.47"
resvg = "0.47"
tiny-skia = "0.12"
# Theme manifest format. RON keeps the file human-editable while
# preserving Rust-style structures the importer can validate.
ron = "0.12"
# Importer-only: reads user-supplied theme zip archives, validates
# their contents, and unpacks them into the user themes directory.
# Default features are disabled to keep the dependency footprint small;
# only `deflate` is needed because the importer rejects other
# compression methods anyway (see Phase 7 spec).
zip = { version = "8.6", default-features = false, features = ["deflate"] }
# Importer-only test dependency: tests build zip archives in a
# scratch directory so they don't pollute the real user themes path
# on the developer's machine.
tempfile = "3.27"
axum = "0.8" axum = "0.8"
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] } sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
jsonwebtoken = { version = "10", default-features = false, features = ["rust_crypto"] } jsonwebtoken = { version = "10", default-features = false, features = ["rust_crypto"] }
+10
View File
@@ -68,6 +68,16 @@ cargo test -p solitaire_core -p solitaire_sync -p solitaire_data -p solitaire_se
cargo clippy --workspace -- -D warnings cargo clippy --workspace -- -D warnings
``` ```
## Credits
Built on [Bevy](https://bevyengine.org/) and the wider Rust ecosystem (Tokio,
Axum, sqlx, Serde, kira, and many more). Card faces come from
[hayeah/playing-cards-assets](https://github.com/hayeah/playing-cards-assets)
(MIT, derived from the public-domain `vector-playing-cards` library); the
default card back is original work; the UI font is FiraMono-Medium (OFL).
All audio is synthesized programmatically by this project. See
[CREDITS.md](CREDITS.md) for the full list and license details.
## License ## License
MIT — see [LICENSE](LICENSE). MIT — see [LICENSE](LICENSE).
+83 -257
View File
@@ -1,284 +1,110 @@
# Solitaire Quest — UX Overhaul Session Handoff # Solitaire Quest — UX Overhaul Session Handoff
**Last updated:** 2026-04-30 — Phase 3 complete + Phase 4 in progress (Track B landed on disk, Track G subset in flight via background agent). **Last updated:** 2026-05-02 (session 7) — UX iteration round complete: every item from session 6's UX punch list has shipped, plus a font-fallback fix surfaced by a second-machine smoke test. Six commits on top of session 6's `c4970b1`. Direction now opens for the next round — release prep or another UX pass, the player's call.
## ⚠️ In-progress work at pause time ## Status at pause
Smoke-test passed; Phase 4 was started. Pushed HEAD is `534870a`. The working tree has **uncommitted** work that is NOT pushed: - **HEAD:** `655dfde`. Local master is **3 commits ahead** of `origin/master` (`f6c9166`, `f712b89`, `655dfde` unpushed; `fdb6c2e` and `95df542` already pushed).
- **Working tree:** clean. (`CARD_PLAN.md` is untracked but intentionally so — it's a plan doc, not source.)
- **Build:** `cargo clippy --workspace --all-targets -- -D warnings` clean.
- **Tests:** **982 passed / 0 failed** across the workspace (+20 from session 6's 962 baseline).
- **Tags on origin:** `v0.9.0`, `v0.10.0`. Stale local-only `v0.1.0` is still safe to `git tag -d v0.1.0`.
### Track B — window polish (on disk, ready to commit) ## Where we are
- **File:** `solitaire_app/src/main.rs` (+44 lines) Session 6's UX punch list was four items. All four shipped today, plus an unrelated font-fallback fix from a second-machine smoke test.
- **What landed:**
- X11/Wayland WM_CLASS via `Window::name = Some("solitaire-quest".into())`
- Default position `WindowPosition::Centered(MonitorSelection::Primary)`
- `install_crash_log_hook()` wraps the default panic hook to also append a `crash.log` next to `settings.json`. Uses `std::time::SystemTime` (no new chrono dep). Falls through silently if the data dir is unavailable.
- **Skipped this round (deferred):**
- App icon hookup — no artwork asset exists yet; add the loader path when art lands.
- Persisted window geometry — needs a `Settings` schema migration.
- F11 fullscreen toggle — already wired in `input_plugin.rs:114`, no change needed.
- **Build status:** `cargo build -p solitaire_app` clean; `cargo clippy -p solitaire_app -- -D warnings` clean.
- **Suggested commit subject:** `feat(app): window polish — class name, centered position, crash-log hook`
### Track G subset — modal open animation + score-change feedback (in flight) The card-theme system, HUD restructure, modal scaffold, and the four big UX feel items (foundations, drop shadows, drop highlights, stock badge) are all in. Direction is open — the deferred release-prep items (`v0.11.0` cut, README/CHANGELOG refresh, desktop packaging) are still on the table, and a fresh round of UX iteration is also available.
- A **background agent** (`general-purpose`, no worktree) was launched against this turn's tree to: ### Design direction (unchanged)
- Extend `spawn_modal` in `solitaire_engine/src/ui_modal.rs` with a `ModalEntering` component + `advance_modal_enter` system that animates scrim alpha 0 → `SCRIM` and card scale 0.96 → 1.0 over `MOTION_MODAL_SECS`. Respects `AnimSpeed::Instant` via `scaled_duration`. Animate-OUT path is intentionally out of scope.
- In `solitaire_engine/src/hud_plugin.rs`, add a `ScorePulse` 1.0→1.1→1.0 readout pulse over `MOTION_SCORE_PULSE_SECS` and a floating "+N" Text2d (only for ≥ +50 jumps) that drifts up ~40 px and fades over `MOTION_SCORE_PULSE_SECS * 2`.
- Tests for both behaviours.
- **State at pause:** the agent had partial edits in `solitaire_engine/src/ui_modal.rs` (visible via `git status`) — at least one unused-import warning was already surfacing. It had not reported back when this snapshot was taken.
- **Resume options for the next session:**
1. **Wait for the notification.** The agent runs in background; if Claude Code is still alive, the completion notification will fire.
2. **Inspect and finish manually.** `git diff solitaire_engine/src/ui_modal.rs solitaire_engine/src/hud_plugin.rs` to see what landed; finish or revert and restart with a tighter prompt.
3. **Discard and restart.** `git restore solitaire_engine/src/ui_modal.rs solitaire_engine/src/hud_plugin.rs` then relaunch the agent with the prompt below.
### Next-session workflow at pause
1. Verify the workspace builds cleanly with **all** in-flight changes: `cargo build --workspace && cargo clippy --workspace -- -D warnings && cargo test --workspace`. The Track B `main.rs` change is independent — even if Track G is reverted, B compiles on its own.
2. If Track B is clean and Track G is incomplete or broken: commit Track B first using the subject above, then deal with Track G.
3. If both are clean: commit each as a separate landing — one feature per commit per project convention.
4. Use:
```
git -c user.name=funman300 -c user.email=root@vscode.infinity commit -m "<subject>"
```
5. Push with `git push origin master` (requires interactive credentials on `git.aleshym.co`).
### Original Track G subset prompt (for relaunch if needed)
The agent's full brief is preserved here verbatim — paste into a fresh agent if the current one is unrecoverable:
```
Two UI/UX polish items from track G. Tree clean at HEAD `534870a`.
Sub-agents CANNOT git commit — stage your work; orchestrator commits.
G1. Modal open animation: extend spawn_modal in ui_modal.rs with a
ModalEntering component + advance_modal_enter system that animates
scrim alpha 0 → SCRIM and card scale 0.96 → 1.0 over MOTION_MODAL_SECS.
Use scaled_duration for AnimSpeed respect; ease-out curve t*(2-t).
Register the system in UiModalPlugin::build. Animate-OUT is OUT of
scope. Add ≥2 tests covering ModalEntering presence on spawn and
removal after duration elapses.
G2. Score-change feedback in hud_plugin.rs: ScorePulse component that
scales the score Text 1.0→1.1→1.0 over MOTION_SCORE_PULSE_SECS using
triangular curve. Plus a floating "+N" Text2d (only for ≥ +50 jumps)
in ACCENT_PRIMARY that drifts up 40 px and fades over
MOTION_SCORE_PULSE_SECS * 2. Add ≥2 tests for floater spawn on +50
and despawn after lifetime, plus ≥1 test that +5 does NOT spawn.
Hard requirements: workspace build + clippy --workspace -- -D warnings
+ test --workspace all green. Touch ONLY ui_modal.rs, hud_plugin.rs,
optionally ui_theme.rs for new tokens (don't think you'll need any).
DO NOT touch solitaire_app/src/main.rs (parallel work).
```
---
## Where we are (Phase 3)
Phase 3 of the UX overhaul brief is **done**. The whole engine has been migrated to the `ui_theme` design-token system + `ui_modal` scaffold. Animation system upgraded. Final literal sweep landed. The work spans 17 commits this session, from the foundation (`e14852c`) through to the final sweep (`54e024c`).
### Design direction (already saved as project memory)
- **Tone:** Balatro — chunky readable type, theatrical hierarchy, satisfying micro-interactions. - **Tone:** Balatro — chunky readable type, theatrical hierarchy, satisfying micro-interactions.
- **Palette:** Midnight Purple base (`BG_BASE` `#1A0F2E` → `BG_ELEVATED` `#2D1B69` → `BG_ELEVATED_HI` `#3A2580` → `BG_ELEVATED_TOP` `#482F97`) + Balatro yellow primary accent (`ACCENT_PRIMARY` `#FFD23F`) + warm magenta secondary (`ACCENT_SECONDARY` `#FF6B9D`). - **Palette:** Midnight Purple base + Balatro yellow primary + warm magenta secondary.
- See [memory/project_ux_overhaul_2026-04.md](.claude/projects/-home-manage-Rusty-Solitare/memory/project_ux_overhaul_2026-04.md) for the full direction. - See `~/.claude/projects/-home-manage-Rusty-Solitare/memory/project_ux_overhaul_2026-04.md` (auto-memory; on a different machine, recreate this fresh from the README + ARCHITECTURE.md).
### Top complaints from the original smoke test — all closed ### Canonical remote
1. **HUD too cluttered.** ✅ Closed by `73cad7e` — readouts now sit in a 4-tier vertical stack with progressive disclosure of penalty/bonus tiers. `github.com/funman300/Rusty_Solitaire` is the canonical repo. Always push there. (Earlier sessions used `Rusty_Solitare` — single-i typo — as the repo name; the rename to `Rusty_Solitaire` happened in session 7. Local clone directories may still be named `Rusty_Solitare`; that's just a directory name and works fine.)
2. **Y/N keyboard prompts feel like debug panels.** ✅ Closed across Confirm, GameOver, Pause, Forfeit, and Settings modals — every prompt now has real Primary/Secondary/Tertiary buttons with hover/press feedback.
## Foundation (done) ## Session 7 (shipped 2026-05-02)
- **`solitaire_engine/src/ui_theme.rs`** — every design token: colours, 5-rung typography scale, 4-multiple spacing scale, three radius rungs, monotonically-ordered z-index hierarchy, motion durations with `scaled_duration(speed)` helper. | Area | Commit | What landed |
- **`solitaire_engine/src/ui_modal.rs`** — `spawn_modal` scaffold + `spawn_modal_header` / `spawn_modal_body_text` / `spawn_modal_actions` / `spawn_modal_button` helpers + `ButtonVariant` enum (Primary / Secondary / Tertiary) + `paint_modal_buttons` system. `UiModalPlugin` registered in `solitaire_app/src/main.rs`. |---|---|---|
| Font fallback | `fdb6c2e` | `shared_fontdb` now `include_bytes!()`s `assets/fonts/main.ttf` (FiraMono) and pins every CSS generic to `"Fira Mono"` so unmatched named families on minimal Linux installs / fresh Wayland sessions / chroots don't drop card rank/suit text. Surfaced when a second-machine pull rendered cards without glyphs. |
| Unlock foundations | `95df542` | `PileType::Foundation(Suit)``Foundation(u8)` (slot 0..3). `Pile::claimed_suit()` derives the claim from the bottom card — no separate field, no claim-stuck-after-undo class of bugs. `can_place_on_foundation` drops its suit parameter. `next_auto_complete_move` prefers a slot whose claimed suit matches the candidate before falling back to the first empty slot for an Ace. Empty foundation markers render as plain placeholders (no "C/D/H/S"). HUD selection label and hint toast read `claimed_suit()` and fall through to "Foundation N" / "move to foundation" when the slot is empty. Save-format invalidation: `GameState.schema_version` bumped 1 → 2; old `game_state.json` files silently fall through to "fresh game on launch." Stats / progress / achievements / settings live in separate files and are unaffected. 9 new tests. |
| Drop overlay | `f6c9166` | The pre-existing `update_drop_highlights` system tinted `PileMarker` sprites green for valid drops, but markers were occluded by stacked cards — invisible during real play. New `update_drop_target_overlays` spawns a soft-fill + 3 px outlined box ABOVE cards for every legal target (full fanned column for tableaux, card-sized for foundations / empty tableaux). `Z_DROP_OVERLAY = 50` sits above static cards but below `DRAG_Z = 500` so the dragged card never gets occluded. Reuses `STATE_SUCCESS` hue. The original marker-tint system is untouched. 3 new tests. |
| Drop shadows | `f712b89` | Each `CardEntity` spawns a `CardShadow` child sprite — neutral black at 25 % alpha, sized `card_size + 4 px`, offset `(2, -3)`, local z `-0.05`. `update_card_shadows_on_drag` snaps shadows in `DragState.cards` to a lifted state (40 % alpha, `(4, -6)` offset, `(8, 8)` padding). `resize_cards_in_place` extended to keep shadows cheap on window resize. `update_card_entity`'s `despawn_related` is followed by a fresh `add_card_shadow_child` so flips / theme swaps re-attach shadows. Pure `card_shadow_params(is_dragged)` helper unit-tested. 4 new tests. |
| Stock badge | `655dfde` | A small `·N` chip at the top-right corner of the stock pile shows the remaining count. `update_stock_count_badge` spawns a top-level world entity whose `Transform.translation` is recomputed each tick from `LayoutResource`, so window resize / theme swap don't strand it. Hides via `Visibility::Hidden` when the stock empties — the existing `↺` `StockEmptyLabel` takes over and they never co-render. `Z_STOCK_BADGE = 30` sits between cards and `Z_DROP_OVERLAY`. 4 new tests. |
## Commits this session (Phase 3, latest first) ## Open punch list — release prep (still deferred unless player chooses now)
1. **Cut `v0.11.0`** — meaningful slice since `v0.10.0`: full card-theme system (CARD_PLAN phases 17 + theme picker + hayeah art), HUD overhaul (band + fade), session 6's four bug fixes, and session 7's font fallback + four UX feel wins. (`git tag -d v0.1.0` first to clean up the stale local tag.)
2. **README + CHANGELOG refresh** — README was last touched at `a6b8348` before the Settings picker shipped; doesn't mention card themes, the auto-fade, or any of session 7's UX work.
3. **Desktop packaging** per `ARCHITECTURE.md §17`. The Arch PKGBUILD exists in `/home/manage/solitaire-quest-pkgbuild/` (separate repo, no remote yet). Pending: app icon, macOS `.icns` + notarisation cert, Windows `.ico` + Authenticode cert, AppImage recipe.
## Open punch list — UX iteration (next-round candidates)
The session-6 list is exhausted. Candidates for a next round, none formally requested by the player:
- **Animated focus ring** (currently a static overlay; could pulse on focus change).
- **Achievement onboarding pass** — show first-time players the achievement panel after their first win.
- **Mode-switch keyboard shortcut** from inside the Mode Launcher (today only mouse opens it).
- **Runtime aspect-ratio fidelity** — hayeah SVGs are ~1.45 h/w; engine layout assumes 1.4. Cards render ~3 % squashed vertically. Cosmetic.
- **Foundation completion celebration** — when a foundation reaches its King, do a small flourish (sparkle, lift, sound). The auto-complete cascade already covers the win moment, but per-foundation closure is currently silent.
- **Drag-cancel return animation** — illegal drops snap cards back instantly. A short ease-back tween ("springs back to where it came from") would feel more forgiving.
## Card-theme system (CARD_PLAN.md, fully shipped)
Seven phases landed across `b8fb3fb``924a1e2`. End-to-end:
- **Bundled default theme** ships in the binary via `embedded://` — 52 hayeah/playing-cards-assets SVGs (MIT) + a midnight-purple `back.svg` (original work).
- **User themes** live under `themes://` rooted at `solitaire_engine::assets::user_theme_dir()`. Drop a directory containing `theme.ron` + 53 SVG files; appears in the registry on next launch.
- **Importer** at `solitaire_engine::theme::import_theme(zip)` validates an archive (20 MB cap, zip-slip rejection, manifest validation, every SVG round-tripped through the rasteriser) and atomically unpacks.
- **Picker UI** in Settings → Cosmetic — one chip per registered theme; selection persists to `settings.json` as `selected_theme_id` and propagates to live sprites via `react_to_settings_theme_change``sync_card_image_set_with_active_theme``StateChangedEvent`.
## Resume prompt
``` ```
54e024c chore(engine): final literal-to-token sweep You are a senior Rust + Bevy developer working on Solitaire Quest.
3a01318 feat(engine): upgrade animations — curves, scoped settle, deal jitter, cascade rotation Working directory: <Rusty_Solitaire clone path on this machine — local
79d3917 chore(data): derive Copy on AnimSpeed directory may still be named Rusty_Solitare from earlier; that's fine>.
ba019c0 feat(engine): convert SettingsPanel to modal scaffold + Done button Branch: master. Direction is OPEN — the session-6 UX punch list is
18d7c12 feat(engine): convert OnboardingPlugin to 3-slide modal flow fully shipped. The player will choose between cutting v0.11.0, doing
cb93bd9 fix(engine): pin modals via GlobalZIndex and surface forfeit-no-op toast release prep (README/CHANGELOG/packaging), or starting a new UX
6723416 feat(engine): convert PauseScreen to modal + add ForfeitConfirmScreen iteration round.
afb0879 docs: add SESSION_HANDOFF.md mid-overhaul checkpoint
3b619b8 feat(engine): convert HomeScreen to modal scaffold + Done button
37681cf feat(engine): convert LeaderboardScreen to modal scaffold + Done button
99064ce feat(engine): convert ProfileScreen to modal scaffold + Done button
de4dba6 feat(engine): convert AchievementsScreen to modal scaffold + Done button
75fc3aa feat(engine): convert StatsScreen to modal scaffold + Done button
deb034c feat(engine): convert HelpScreen to real-button modal with kbd-chip rows
242b5fe feat(engine): convert GameOverScreen to real-button modal
3f922ed feat(engine): convert ConfirmNewGameScreen to real-button modal
8da62bd feat(engine): add ui_modal primitive (scaffold + button variants)
73cad7e feat(engine): restructure HUD into 4-tier layout, adopt design tokens
e14852c feat(engine): add ui_theme.rs design-token module
```
**Test status:** `cargo build --workspace` clean, `cargo clippy --workspace -- -D warnings` clean, **819 tests pass / 0 failed / 8 ignored**. State: HEAD=655dfde. Local master is 3 commits ahead of origin
(f6c9166, f712b89, 655dfde unpushed; fdb6c2e and 95df542 already
## Smoke-test checklist pushed). Working tree clean apart from untracked CARD_PLAN.md
(intentional).
The whole overhaul is on disk. Worth running through once end-to-end: Build: cargo clippy --workspace --all-targets -- -D warnings clean.
Tests: 982 passed / 0 failed.
1. **Run the game.** `cargo run -p solitaire_app --features bevy/dynamic_linking`.
2. **HUD layout** reads as 4 stacked tiers (Score / Mode / Penalty / Selection) with the new midnight-purple palette.
3. **Open every overlay** — `S` (Stats), `A` (Achievements), `P` (Profile), `O` (Settings), `L` (Leaderboard), `M` (Home), `F1` (Help). Each is a centred card on a uniform scrim with a yellow `Done` / `Close` primary button. Hover/press states on every button.
4. **Settings.** Four sections (Audio / Gameplay / Cosmetic / Sync). Body scrolls within the modal on small windows; `Done` button stays fixed at the bottom regardless of scroll. Card-back / Background pickers tint the selected swatch with `STATE_SUCCESS`.
5. **Confirm flow.** Click `New Game` while a game is in progress — the abandon-current-game modal has real Cancel/Confirm buttons. `Y/Enter` and the yellow primary button start a new game; `N/Esc` and the secondary button cancel.
6. **Pause + Forfeit.** Press `Esc` — pause modal shows real Resume / Forfeit buttons. Forfeit button opens a Cancel/Forfeit confirmation modal stacked above the pause modal (z-index ordered correctly via `GlobalZIndex`).
7. **First-run onboarding.** Delete `settings.json` (or set `first_run_complete = false`) — three-slide flow shows: Welcome → How to play → Keyboard shortcuts. Navigate with `Next` / `Back` buttons or `` / `` accelerators. `Esc` skips on slide 0.
8. **Animations.**
- Slide a card to a pile — motion curves through `SmoothSnap` (slight overshoot + settle), not linear lerp.
- Drop a card on a valid destination — only the moved cards bounce; the rest of the table stays still.
- Start a new game — deal stagger is no longer mechanically uniform; cards land with subtle ±10% timing variation.
- Win a game — cascade now uses `Expressive` curve with per-card ±15° Z-rotation, screen shake driven by the new `MOTION_WIN_SHAKE_*` tokens.
9. **Resize the window** — cards still snap, no "snap-back-and-forth" jitter.
10. **Win modal** — restyled with the design tokens: midnight-purple card, yellow `Play Again` button.
## Open follow-ups (not blockers)
- **Home / Help redundancy.** Home is still a kbd-reference modal that mostly duplicates Help. Three options: (1) keep as-is, (2) convert into a true mode launcher (Classic / Daily / Zen / Challenge / Time Attack cards, locked options visibly disabled below level 5), (3) drop entirely now that the action bar covers everything Home does. Worth asking the user which direction they want.
- **Forfeit countdown toast** is now superseded by the Forfeit modal (`6723416`). Confirm the toast path is no longer reachable when smoke-testing.
- **Sub-rung pixel sizes** (1 px borders, 64/80/110/150/160 px fixed widths, 28/36/50 px specific spacings) were intentionally left as literals during the step-10 sweep — they're below the smallest `SPACE_*` rung. If the design system grows a "fine" spacing tier in the future, those become candidates for migration.
## Resume prompt for the next session
```
You are a senior Rust + Bevy developer working toward a public release
of Solitaire Quest. Working directory: /home/manage/Rusty_Solitare.
Branch: master. Apply that lens to every decision: prefer shipping
quality (polish, packaging, defaults, credits, crash safety) over
greenfield features. If something is half-done, the question is
"finish for v1 or cut for v1?" not "what else can we add?".
State: HEAD=0066ca6. Phase 3 of the UX overhaul is shipped. cargo
build / clippy --workspace -- -D warnings / test --workspace all
green — 819 tests pass / 0 fail / 8 ignored.
READ FIRST (in order, before doing anything): READ FIRST (in order, before doing anything):
1. SESSION_HANDOFF.md — full state, smoke-test checklist, follow-ups 1. SESSION_HANDOFF.md — full state, session 7 changelog, punch list
2. CLAUDE.md — hard rules (UI-first, no panics, etc.) 2. CLAUDE.md — hard rules (UI-first, no panics, etc.)
3. ARCHITECTURE.md §1, §15, §17 — design principles, platform 3. ARCHITECTURE.md — crate responsibilities + data flow
targets, deployment guide 4. ~/.claude/projects/<this-project>/memory/MEMORY.md
4. ~/.claude/projects/-home-manage-Rusty-Solitare/memory/MEMORY.md — saved feedback / project context (machine-local;
— saved feedback / project context may be missing on a fresh machine)
GATING SIGNAL — ASK FIRST, DON'T ASSUME: DECISION TO ASK THE PLAYER FIRST:
Before proposing new work, ask: "Did the smoke-test (items 1-10 in A. Push the 3 unpushed commits and cut v0.11.0?
SESSION_HANDOFF.md) pass, or did anything regress?" If a regression B. Skip the tag for now, refresh README + CHANGELOG, then tag?
exists, fix it before opening any new thread. C. Skip release prep entirely and start a new UX iteration round?
If C, see the session-7 next-round candidates list (animated
LIKELY NEXT DIRECTIONS — surface for the user to choose, don't pick focus ring, achievement onboarding, mode-switch keyboard
unilaterally. All framed through "what does v1 release need?": shortcut, aspect-ratio fidelity, foundation completion flourish,
drag-cancel return tween).
A. Home modal decision (open in SESSION_HANDOFF.md).
- keep as kbd-reference (duplicates Help — release-blocking
confusion?)
- repurpose as mode launcher (Classic / Daily / Zen / Challenge /
Time Attack cards, locked options below level 5)
- drop (action bar already covers every action)
B. Window + release polish — `solitaire_app/src/main.rs:34-48`
currently sets only title + resolution + min size. For public
release the window needs:
- app icon (taskbar / dock / alt-tab) — Bevy `Window::window_icon`
or platform `set_window_icon`; ship a .png/.ico asset.
- window class / app id (`Window::name`) so X11/Wayland and
Windows group taskbar entries correctly.
- persist size + position across launches (Settings already
saves to JSON; add `window_geometry` field).
- F11 (or a Settings toggle) wired to real fullscreen mode.
- centered default position on first launch (Bevy supports
`WindowPosition::Centered`).
- present_mode + vsync verification — make sure Linux/macOS
don't ship at uncapped 4000 fps.
- panic hook (`std::panic::set_hook`) that writes a crash
report next to the save files instead of silently exiting.
- macOS Info.plist / Windows .ico bundling — ARCHITECTURE.md
§17 currently only covers server deploy.
C. Sound-design audit. The scoped settle bounce (3a01318) means
audio_plugin.rs trigger sites may fire less often than before;
verify card_place / card_flip / card_invalid still feel right.
D. Sync flow end-to-end on a real second machine. Server
scaffolding exists but the register → push → pull → restore-on-
other-device round trip hasn't been exercised against the new
Settings sync section.
E. Achievement unlock completeness. ARCHITECTURE.md §11 lists 18.
The three hidden ones (speed_and_skill, comeback, zen_winner)
are most likely to be untested. For release, every advertised
achievement needs to actually fire.
F. Release-readiness backlog:
- README / store-page copy / screenshots
- LICENSE + third-party credits (xCards art, FiraMono, Bevy)
- SemVer + a v0.1.0 git tag
- itch.io / Steam packaging per platform (ARCHITECTURE.md §15)
- App signing — macOS notarization, Windows Authenticode,
Linux AppImage
- Telemetry / crash reporting — opt-in, off by default; or
confirm we ship without and rely on player reports
G. UI/UX professional polish — Phase 3 shipped the design system;
v1 wants the difference between "consistent" and "feels
intentional":
- Microcopy pass: every button label, empty state, error
message, and onboarding line reviewed for voice + clarity.
Pick one verb per concept ("Done" vs "Close" vs "OK") and
apply it everywhere.
- Empty / loading / error states: Leaderboard before any
scores, Stats before any games, Sync UI before login.
Today these are likely blank panels.
- Modal open/close animation: `MOTION_MODAL_SECS` token exists
in `ui_theme.rs:255` but isn't wired up — modals
appear/disappear instantly. Add scale-from-0.96 + scrim fade
per the token's doc comment.
- Tooltips on HUD readouts and settings labels. Bevy has no
built-in tooltip; build a small one. Hover a number to learn
what it counts.
- Accessibility: verify the AAA-contrast claim on
`ACCENT_PRIMARY` over `BG_BASE` (ui_theme.rs:65). Confirm
`AnimSpeed::Instant` disables every new animation (slide
curve, scoped settle, deal jitter, cascade rotation). Add
focus rings on `Button` entities for keyboard navigation.
- Typography choice: FiraMono is one weight, monospace for
everything. Consider shipping a second proportional face for
body + headings, keep mono for numerics (HUD score, timer).
Or commit to mono and lean into the "calm coder" feel — pick
deliberately and document the decision.
- Onboarding artwork: the 3 slides are text + buttons. For
release, stylised illustrations (or simple animated card
props on each slide) elevate the first-launch feel.
- Score-change feedback: floating "+N" numbers when score
jumps; pulse on the readout when value crosses a milestone.
`MOTION_SCORE_PULSE_SECS` is already a token.
- Splash / loading screen: today the window goes straight to
gameplay. A 1-2 second branded splash signals "real game"
vs "rust prototype".
- Hit-target audit: every interactive element ≥ 32 px on
desktop. Settings has 28 px icon buttons (`ICON_BUTTON_PX`
in settings_plugin.rs); revisit.
- Win-moment design: the cascade is good; consider a score-
breakdown reveal, streak callout, "share your time"
affordance for v1.
WORKFLOW NOTES: WORKFLOW NOTES:
- Commits use: - Commits use:
git -c user.name=funman300 -c user.email=root@vscode.infinity commit -m "..." git -c user.name=funman300 -c user.email=root@vscode.infinity \
- Sub-agents can Edit/Write but CANNOT `git commit`. Brief them to commit -m "..."
stage + verify only; orchestrator commits on their behalf. - Sub-agents stage + verify only; orchestrator commits.
See memory/feedback_agent_commit_limit.md. - Every commit must pass build / clippy / test before pushing.
- Remote push needs interactive credentials on git.aleshym.co; the - Push to GitHub (origin) — that is the canonical remote.
user runs `git push origin master` themselves.
- Every commit must pass build / clippy / test. Pause-and-verify
is the user's preferred cadence — one feature per commit.
OPEN AT THE START: ask (1) did smoke-test pass, (2) which of AG to OPEN AT THE START: ask which of A / B / C. Don't pick unilaterally —
pursue first. Do not assume. this is a directional choice, not a tactical one.
``` ```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 196 KiB

+1 -1
View File
@@ -1,7 +1,7 @@
# Solitaire Quest — Session Handoff # Solitaire Quest — Session Handoff
> Last updated: 2026-04-25 > Last updated: 2026-04-25
> Branch: `master` — pushed to https://git.aleshym.co/funman300/Rusty_Solitare.git > Branch: `master` — pushed to https://github.com/funman300/Rusty_Solitaire.git
> Test count: **242 passing** (83 core + 60 data + 99 engine), `cargo clippy --workspace -- -D warnings` clean > Test count: **242 passing** (83 core + 60 data + 99 engine), `cargo clippy --workspace -- -D warnings` clean
--- ---
+50 -12
View File
@@ -3,15 +3,16 @@ use std::io::Write;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use bevy::prelude::*; use bevy::prelude::*;
use bevy::window::{MonitorSelection, WindowPosition}; use bevy::window::{MonitorSelection, PresentMode, WindowPosition};
use solitaire_data::{load_settings_from, provider_for_backend, settings_file_path, Settings}; use solitaire_data::{load_settings_from, provider_for_backend, settings_file_path, Settings};
use solitaire_engine::{ use solitaire_engine::{
AchievementPlugin, AnimationPlugin, AudioPlugin, AutoCompletePlugin, CardAnimationPlugin, register_theme_asset_sources, AchievementPlugin, AnimationPlugin, AssetSourcesPlugin,
CardPlugin, ChallengePlugin, CursorPlugin, DailyChallengePlugin, FeedbackAnimPlugin, AudioPlugin, AutoCompletePlugin, CardAnimationPlugin, CardPlugin, ChallengePlugin,
FontPlugin, GamePlugin, HelpPlugin, HomePlugin, HudPlugin, InputPlugin, LeaderboardPlugin, CursorPlugin, DailyChallengePlugin, FeedbackAnimPlugin, FontPlugin, GamePlugin, HelpPlugin,
OnboardingPlugin, PausePlugin, ProfilePlugin, ProgressPlugin, SelectionPlugin, SettingsPlugin, HomePlugin, HudPlugin, InputPlugin, LeaderboardPlugin, OnboardingPlugin, PausePlugin,
StatsPlugin, SyncPlugin, TablePlugin, TimeAttackPlugin, UiModalPlugin, WeeklyGoalsPlugin, ProfilePlugin, ProgressPlugin, SelectionPlugin, SettingsPlugin, SplashPlugin, StatsPlugin,
WinSummaryPlugin, SyncPlugin, TablePlugin, ThemePlugin, ThemeRegistryPlugin, TimeAttackPlugin, UiFocusPlugin,
UiModalPlugin, UiTooltipPlugin, WeeklyGoalsPlugin, WinSummaryPlugin,
}; };
fn main() { fn main() {
@@ -39,7 +40,32 @@ fn main() {
.unwrap_or_default(); .unwrap_or_default();
let sync_provider = provider_for_backend(&settings.sync_backend); let sync_provider = provider_for_backend(&settings.sync_backend);
App::new() // Restore the previous window geometry if the player has one saved.
// Otherwise open at the platform default (1280×800, centred on the
// primary monitor). The window_geometry field is None on first run
// and after upgrading from a build that didn't persist geometry.
let (window_resolution, window_position) = match settings.window_geometry {
Some(geom) => (
(geom.width, geom.height).into(),
WindowPosition::At(IVec2::new(geom.x, geom.y)),
),
None => (
(1280u32, 800u32).into(),
WindowPosition::Centered(MonitorSelection::Primary),
),
};
let mut app = App::new();
// The card-theme system's `themes://` asset source must be
// registered *before* `DefaultPlugins` builds `AssetPlugin`,
// because that plugin freezes the asset-source list at build
// time. The matching `AssetSourcesPlugin` (added below) finishes
// the wiring after `DefaultPlugins` by populating the embedded
// default theme into Bevy's `EmbeddedAssetRegistry`.
register_theme_asset_sources(&mut app);
app
.add_plugins( .add_plugins(
DefaultPlugins DefaultPlugins
.set(WindowPlugin { .set(WindowPlugin {
@@ -48,8 +74,15 @@ fn main() {
// X11/Wayland WM_CLASS so taskbar managers group // X11/Wayland WM_CLASS so taskbar managers group
// multiple windows of this app correctly. // multiple windows of this app correctly.
name: Some("solitaire-quest".into()), name: Some("solitaire-quest".into()),
resolution: (1280u32, 800u32).into(), resolution: window_resolution,
position: WindowPosition::Centered(MonitorSelection::Primary), position: window_position,
// AutoNoVsync prefers Mailbox (triple-buffered) and
// falls back to Immediate, eliminating the vsync stall
// that AutoVsync produces during continuous window
// resize on X11 / Wayland. The game's frame budget is
// small enough that a few stray dropped frames from
// disabling vsync are imperceptible.
present_mode: PresentMode::AutoNoVsync,
resize_constraints: bevy::window::WindowResizeConstraints { resize_constraints: bevy::window::WindowResizeConstraints {
min_width: 800.0, min_width: 800.0,
min_height: 600.0, min_height: 600.0,
@@ -69,6 +102,9 @@ fn main() {
..default() ..default()
}), }),
) )
.add_plugins(AssetSourcesPlugin)
.add_plugins(ThemePlugin)
.add_plugins(ThemeRegistryPlugin)
.add_plugins(FontPlugin) .add_plugins(FontPlugin)
.add_plugins(GamePlugin) .add_plugins(GamePlugin)
.add_plugins(TablePlugin) .add_plugins(TablePlugin)
@@ -99,6 +135,9 @@ fn main() {
.add_plugins(LeaderboardPlugin) .add_plugins(LeaderboardPlugin)
.add_plugins(WinSummaryPlugin) .add_plugins(WinSummaryPlugin)
.add_plugins(UiModalPlugin) .add_plugins(UiModalPlugin)
.add_plugins(UiFocusPlugin)
.add_plugins(UiTooltipPlugin)
.add_plugins(SplashPlugin)
.run(); .run();
} }
@@ -124,8 +163,7 @@ fn install_crash_log_hook() {
// parseable and avoids pulling in chrono just for this. // parseable and avoids pulling in chrono just for this.
let secs = SystemTime::now() let secs = SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.map(|d| d.as_secs()) .map_or(0, |d| d.as_secs());
.unwrap_or(0);
let _ = writeln!(file, "----- t={secs} -----\n{info}\n"); let _ = writeln!(file, "----- t={secs} -----\n{info}\n");
} }
default_hook(info); default_hook(info);
-1
View File
@@ -6,6 +6,5 @@ edition.workspace = true
[dependencies] [dependencies]
serde = { workspace = true } serde = { workspace = true }
chrono = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
rand = { workspace = true } rand = { workspace = true }
+204 -24
View File
@@ -1,6 +1,6 @@
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::card::{Card, Suit}; use crate::card::Card;
use crate::deck::{deal_klondike, Deck}; use crate::deck::{deal_klondike, Deck};
use crate::error::MoveError; use crate::error::MoveError;
use crate::pile::{Pile, PileType}; use crate::pile::{Pile, PileType};
@@ -9,6 +9,20 @@ use crate::scoring::{compute_time_bonus as scoring_time_bonus, score_move, score
const MAX_UNDO_STACK: usize = 64; const MAX_UNDO_STACK: usize = 64;
/// Save-file schema version for `GameState`. Increment when the on-disk
/// representation changes incompatibly so `load_game_state_from` can refuse
/// older formats and start the player on a fresh game.
///
/// History:
/// - v1: `Foundation(Suit)` keys.
/// - v2 (current): `Foundation(u8)` slot keys; claimed suit derived from the
/// bottom card of the pile.
pub const GAME_STATE_SCHEMA_VERSION: u32 = 2;
/// Default value for `GameState::schema_version` when deserialising older
/// save files that pre-date the field.
fn schema_v1() -> u32 { 1 }
/// Serialize `HashMap<PileType, Pile>` as a `Vec` of `(key, value)` pairs so /// Serialize `HashMap<PileType, Pile>` as a `Vec` of `(key, value)` pairs so
/// that JSON (which requires string map keys) round-trips correctly. /// that JSON (which requires string map keys) round-trips correctly.
mod pile_map_serde { mod pile_map_serde {
@@ -98,6 +112,11 @@ pub struct GameState {
/// Used by the `comeback` achievement condition. /// Used by the `comeback` achievement condition.
#[serde(default)] #[serde(default)]
pub recycle_count: u32, pub recycle_count: u32,
/// Save-file schema version. Defaults to `1` for older files that pre-date
/// the field. The loader refuses any value other than
/// [`GAME_STATE_SCHEMA_VERSION`].
#[serde(default = "schema_v1")]
pub schema_version: u32,
undo_stack: VecDeque<StateSnapshot>, undo_stack: VecDeque<StateSnapshot>,
} }
@@ -116,8 +135,8 @@ impl GameState {
let mut piles: HashMap<PileType, Pile> = HashMap::new(); let mut piles: HashMap<PileType, Pile> = HashMap::new();
piles.insert(PileType::Stock, stock); piles.insert(PileType::Stock, stock);
piles.insert(PileType::Waste, Pile::new(PileType::Waste)); piles.insert(PileType::Waste, Pile::new(PileType::Waste));
for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] { for slot in 0..4_u8 {
piles.insert(PileType::Foundation(suit), Pile::new(PileType::Foundation(suit))); piles.insert(PileType::Foundation(slot), Pile::new(PileType::Foundation(slot)));
} }
for (i, pile) in tableau.into_iter().enumerate() { for (i, pile) in tableau.into_iter().enumerate() {
piles.insert(PileType::Tableau(i), pile); piles.insert(PileType::Tableau(i), pile);
@@ -135,6 +154,7 @@ impl GameState {
is_auto_completable: false, is_auto_completable: false,
undo_count: 0, undo_count: 0,
recycle_count: 0, recycle_count: 0,
schema_version: GAME_STATE_SCHEMA_VERSION,
undo_stack: VecDeque::new(), undo_stack: VecDeque::new(),
} }
} }
@@ -247,14 +267,14 @@ impl GameState {
let bottom_card = from_pile.cards[start].clone(); let bottom_card = from_pile.cards[start].clone();
match &to { match &to {
PileType::Foundation(suit) => { PileType::Foundation(_) => {
if count != 1 { if count != 1 {
return Err(MoveError::RuleViolation( return Err(MoveError::RuleViolation(
"only one card can move to foundation at a time".into(), "only one card can move to foundation at a time".into(),
)); ));
} }
let dest = self.piles.get(&to).ok_or(MoveError::InvalidDestination)?; let dest = self.piles.get(&to).ok_or(MoveError::InvalidDestination)?;
if !can_place_on_foundation(&bottom_card, dest, *suit) { if !can_place_on_foundation(&bottom_card, dest) {
return Err(MoveError::RuleViolation("invalid foundation placement".into())); return Err(MoveError::RuleViolation("invalid foundation placement".into()));
} }
} }
@@ -332,15 +352,13 @@ impl GameState {
Ok(()) Ok(())
} }
/// Returns `true` when all four foundations each contain 13 cards. /// Returns `true` when all four foundation slots each contain 13 cards.
pub fn check_win(&self) -> bool { pub fn check_win(&self) -> bool {
[Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] (0..4_u8).all(|slot| {
.iter() self.piles
.all(|&suit| { .get(&PileType::Foundation(slot))
self.piles .is_some_and(|p| p.cards.len() == 13)
.get(&PileType::Foundation(suit)) })
.is_some_and(|p| p.cards.len() == 13)
})
} }
/// Returns `true` when stock and waste are empty and all tableau cards are face-up. /// Returns `true` when stock and waste are empty and all tableau cards are face-up.
@@ -379,13 +397,34 @@ impl GameState {
if !self.is_auto_completable || self.is_won { if !self.is_auto_completable || self.is_won {
return None; return None;
} }
let suits = [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades];
for i in 0..7 { for i in 0..7 {
let tableau = PileType::Tableau(i); let tableau = PileType::Tableau(i);
if let Some(card) = self.piles[&tableau].cards.last() { if let Some(card) = self.piles[&tableau].cards.last() {
for &suit in &suits { // Prefer the slot that already claims this card's suit so
let foundation = PileType::Foundation(suit); // Aces don't sometimes land in slot 0 and then leave the
if can_place_on_foundation(card, &self.piles[&foundation], suit) { // matching suit-claimed slot empty.
let mut candidate: Option<u8> = None;
let mut empty_slot: Option<u8> = None;
for slot in 0..4_u8 {
let foundation = PileType::Foundation(slot);
let pile = &self.piles[&foundation];
if pile.cards.is_empty() {
if empty_slot.is_none() {
empty_slot = Some(slot);
}
} else if pile.claimed_suit() == Some(card.suit) {
candidate = Some(slot);
break;
}
}
let target_slot = candidate.or_else(|| {
// Only fall back to an empty slot if the card is an Ace,
// which is the only rank that can claim an empty slot.
if card.rank.value() == 1 { empty_slot } else { None }
});
if let Some(slot) = target_slot {
let foundation = PileType::Foundation(slot);
if can_place_on_foundation(card, &self.piles[&foundation]) {
return Some((tableau, foundation)); return Some((tableau, foundation));
} }
} }
@@ -403,7 +442,7 @@ impl GameState {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::card::{Card, Rank}; use crate::card::{Card, Rank, Suit};
fn new_game() -> GameState { fn new_game() -> GameState {
GameState::new(42, DrawMode::DrawOne) GameState::new(42, DrawMode::DrawOne)
@@ -434,8 +473,8 @@ mod tests {
#[test] #[test]
fn new_game_foundations_are_empty() { fn new_game_foundations_are_empty() {
let g = new_game(); let g = new_game();
for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] { for slot in 0..4_u8 {
assert!(g.piles[&PileType::Foundation(suit)].cards.is_empty()); assert!(g.piles[&PileType::Foundation(slot)].cards.is_empty());
} }
} }
@@ -662,7 +701,7 @@ mod tests {
]; ];
let result = g.move_cards( let result = g.move_cards(
PileType::Tableau(0), PileType::Tableau(0),
PileType::Foundation(Suit::Clubs), PileType::Foundation(0),
2, 2,
); );
assert!( assert!(
@@ -706,8 +745,9 @@ mod tests {
#[test] #[test]
fn win_detection_all_foundations_complete() { fn win_detection_all_foundations_complete() {
let mut g = new_game(); let mut g = new_game();
for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] { let suits = [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades];
let f = g.piles.get_mut(&PileType::Foundation(suit)).unwrap(); for (slot, suit) in suits.into_iter().enumerate() {
let f = g.piles.get_mut(&PileType::Foundation(slot as u8)).unwrap();
f.cards.clear(); f.cards.clear();
for rank in [ for rank in [
Rank::Ace, Rank::Two, Rank::Three, Rank::Four, Rank::Five, Rank::Ace, Rank::Two, Rank::Three, Rank::Four, Rank::Five,
@@ -1039,7 +1079,8 @@ mod tests {
let mv = g.next_auto_complete_move().expect("should find a move"); let mv = g.next_auto_complete_move().expect("should find a move");
assert_eq!(mv.0, PileType::Tableau(0)); assert_eq!(mv.0, PileType::Tableau(0));
assert_eq!(mv.1, PileType::Foundation(Suit::Clubs)); // Slot 0 is the first empty foundation; the Ace lands there.
assert_eq!(mv.1, PileType::Foundation(0));
} }
#[test] #[test]
@@ -1049,4 +1090,143 @@ mod tests {
g.is_won = true; g.is_won = true;
assert!(g.next_auto_complete_move().is_none()); assert!(g.next_auto_complete_move().is_none());
} }
// --- Slot-based foundation behaviour (refactor coverage) ---
/// Aces land in the first empty slot regardless of suit, and successive
/// Aces fan out across slots 0, 1, 2, 3 in deterministic order.
#[test]
fn any_ace_lands_in_first_empty_foundation() {
let mut g = new_game();
// Clear stock/waste/tableau so we can hand-construct moves directly.
g.piles.get_mut(&PileType::Stock).unwrap().cards.clear();
g.piles.get_mut(&PileType::Waste).unwrap().cards.clear();
for i in 0..7 {
g.piles.get_mut(&PileType::Tableau(i)).unwrap().cards.clear();
}
// Place an Ace of Clubs on tableau 0; move it to slot 0.
g.piles.get_mut(&PileType::Tableau(0)).unwrap().cards.push(Card {
id: 1, suit: Suit::Clubs, rank: Rank::Ace, face_up: true,
});
g.move_cards(PileType::Tableau(0), PileType::Foundation(0), 1).unwrap();
// Now place an Ace of Spades on tableau 0 and move it to slot 1.
g.piles.get_mut(&PileType::Tableau(0)).unwrap().cards.push(Card {
id: 2, suit: Suit::Spades, rank: Rank::Ace, face_up: true,
});
g.move_cards(PileType::Tableau(0), PileType::Foundation(1), 1).unwrap();
assert_eq!(g.piles[&PileType::Foundation(0)].claimed_suit(), Some(Suit::Clubs));
assert_eq!(g.piles[&PileType::Foundation(1)].claimed_suit(), Some(Suit::Spades));
}
/// `Pile::claimed_suit` reads the bottom card's suit on a populated
/// foundation slot, regardless of which slot index the pile occupies.
#[test]
fn claimed_suit_is_derived_from_bottom_card() {
let mut g = new_game();
g.piles.get_mut(&PileType::Stock).unwrap().cards.clear();
g.piles.get_mut(&PileType::Waste).unwrap().cards.clear();
for i in 0..7 {
g.piles.get_mut(&PileType::Tableau(i)).unwrap().cards.clear();
}
g.piles.get_mut(&PileType::Tableau(0)).unwrap().cards.push(Card {
id: 50, suit: Suit::Hearts, rank: Rank::Ace, face_up: true,
});
g.move_cards(PileType::Tableau(0), PileType::Foundation(2), 1).unwrap();
assert_eq!(
g.piles[&PileType::Foundation(2)].claimed_suit(),
Some(Suit::Hearts)
);
}
/// Undoing the only card from a foundation slot drops the claimed suit;
/// the slot then accepts a different Ace.
#[test]
fn foundation_claim_drops_when_emptied_via_undo() {
let mut g = new_game();
g.piles.get_mut(&PileType::Stock).unwrap().cards.clear();
g.piles.get_mut(&PileType::Waste).unwrap().cards.clear();
for i in 0..7 {
g.piles.get_mut(&PileType::Tableau(i)).unwrap().cards.clear();
}
g.piles.get_mut(&PileType::Tableau(0)).unwrap().cards.push(Card {
id: 1, suit: Suit::Hearts, rank: Rank::Ace, face_up: true,
});
g.move_cards(PileType::Tableau(0), PileType::Foundation(0), 1).unwrap();
assert_eq!(g.piles[&PileType::Foundation(0)].claimed_suit(), Some(Suit::Hearts));
g.undo().unwrap();
assert!(g.piles[&PileType::Foundation(0)].cards.is_empty());
assert!(g.piles[&PileType::Foundation(0)].claimed_suit().is_none());
// A different Ace can now claim slot 0.
let t0 = g.piles.get_mut(&PileType::Tableau(0)).unwrap();
t0.cards.clear();
t0.cards.push(Card { id: 2, suit: Suit::Spades, rank: Rank::Ace, face_up: true });
g.move_cards(PileType::Tableau(0), PileType::Foundation(0), 1).unwrap();
assert_eq!(g.piles[&PileType::Foundation(0)].claimed_suit(), Some(Suit::Spades));
}
/// Successive Aces from the waste pile distribute across slots 0..=3 in
/// order — the player picks the slot, but `move_cards` accepts any
/// empty-slot placement for an Ace.
#[test]
fn multiple_aces_distribute_across_slots() {
let mut g = new_game();
g.piles.get_mut(&PileType::Stock).unwrap().cards.clear();
g.piles.get_mut(&PileType::Waste).unwrap().cards.clear();
for i in 0..7 {
g.piles.get_mut(&PileType::Tableau(i)).unwrap().cards.clear();
}
let aces = [
(Suit::Clubs, 10),
(Suit::Diamonds, 11),
(Suit::Hearts, 12),
(Suit::Spades, 13),
];
for (slot, (suit, id)) in aces.iter().enumerate() {
g.piles.get_mut(&PileType::Waste).unwrap().cards.push(Card {
id: *id, suit: *suit, rank: Rank::Ace, face_up: true,
});
g.move_cards(PileType::Waste, PileType::Foundation(slot as u8), 1).unwrap();
}
for (slot, (suit, _)) in aces.iter().enumerate() {
assert_eq!(
g.piles[&PileType::Foundation(slot as u8)].claimed_suit(),
Some(*suit),
"slot {slot} should claim {suit:?}",
);
}
}
/// Auto-complete prefers the foundation slot whose claimed suit matches
/// the candidate card's suit, even if an empty slot exists at a lower
/// index.
#[test]
fn next_auto_complete_move_picks_slot_with_matching_claim() {
let mut g = new_game();
g.piles.get_mut(&PileType::Stock).unwrap().cards.clear();
g.piles.get_mut(&PileType::Waste).unwrap().cards.clear();
for i in 0..7 {
g.piles.get_mut(&PileType::Tableau(i)).unwrap().cards.clear();
}
// Slot 0 is empty; slot 1 already claims Hearts via Ace of Hearts.
g.piles.get_mut(&PileType::Foundation(1)).unwrap().cards.push(Card {
id: 1, suit: Suit::Hearts, rank: Rank::Ace, face_up: true,
});
// Tableau 0 holds the 2 of Hearts to play.
g.piles.get_mut(&PileType::Tableau(0)).unwrap().cards.push(Card {
id: 2, suit: Suit::Hearts, rank: Rank::Two, face_up: true,
});
g.is_auto_completable = true;
let mv = g.next_auto_complete_move().expect("auto-complete must find slot 1");
assert_eq!(mv.0, PileType::Tableau(0));
assert_eq!(
mv.1,
PileType::Foundation(1),
"must target the Hearts-claimed slot, not the empty slot 0",
);
}
} }
+38 -5
View File
@@ -8,8 +8,10 @@ pub enum PileType {
Stock, Stock,
/// The face-up discard pile drawn to. /// The face-up discard pile drawn to.
Waste, Waste,
/// One of the four suit-ordered foundation piles. /// One of the four foundation slots (0..=3). The claimed suit, if any,
Foundation(Suit), /// is derived from the bottom card of the pile (always an Ace by
/// construction).
Foundation(u8),
/// One of the seven tableau columns (06). /// One of the seven tableau columns (06).
Tableau(usize), Tableau(usize),
} }
@@ -17,7 +19,7 @@ pub enum PileType {
/// A named collection of cards in a specific board position. /// A named collection of cards in a specific board position.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Pile { pub struct Pile {
/// Which pile this is (Stock, Waste, Foundation suit, or Tableau column). /// Which pile this is (Stock, Waste, Foundation slot, or Tableau column).
pub pile_type: PileType, pub pile_type: PileType,
/// Cards in the pile, bottom-to-top stacking order. Last element is the top card. /// Cards in the pile, bottom-to-top stacking order. Last element is the top card.
pub cards: Vec<Card>, pub cards: Vec<Card>,
@@ -33,6 +35,16 @@ impl Pile {
pub fn top(&self) -> Option<&Card> { pub fn top(&self) -> Option<&Card> {
self.cards.last() self.cards.last()
} }
/// For foundation piles: returns `Some(suit)` once at least one card has
/// landed (the bottom card is always an Ace of the claimed suit).
/// Returns `None` for empty foundations or non-foundation piles.
pub fn claimed_suit(&self) -> Option<Suit> {
match self.pile_type {
PileType::Foundation(_) => self.cards.first().map(|c| c.suit),
_ => None,
}
}
} }
#[cfg(test)] #[cfg(test)]
@@ -61,12 +73,33 @@ mod tests {
} }
#[test] #[test]
fn pile_type_foundation_uses_suit() { fn pile_type_foundation_uses_slot_index() {
assert_ne!(PileType::Foundation(Suit::Hearts), PileType::Foundation(Suit::Spades)); assert_ne!(PileType::Foundation(0), PileType::Foundation(3));
} }
#[test] #[test]
fn pile_type_tableau_uses_index() { fn pile_type_tableau_uses_index() {
assert_ne!(PileType::Tableau(0), PileType::Tableau(6)); assert_ne!(PileType::Tableau(0), PileType::Tableau(6));
} }
#[test]
fn claimed_suit_is_none_for_empty_foundation() {
let pile = Pile::new(PileType::Foundation(0));
assert!(pile.claimed_suit().is_none());
}
#[test]
fn claimed_suit_is_none_for_non_foundation() {
let mut pile = Pile::new(PileType::Tableau(0));
pile.cards.push(Card { id: 0, suit: Suit::Hearts, rank: Rank::Ace, face_up: true });
assert!(pile.claimed_suit().is_none());
}
#[test]
fn claimed_suit_returns_bottom_card_suit() {
let mut pile = Pile::new(PileType::Foundation(2));
pile.cards.push(Card { id: 0, suit: Suit::Hearts, rank: Rank::Ace, face_up: true });
pile.cards.push(Card { id: 1, suit: Suit::Hearts, rank: Rank::Two, face_up: true });
assert_eq!(pile.claimed_suit(), Some(Suit::Hearts));
}
} }
+37 -26
View File
@@ -1,16 +1,18 @@
use crate::card::{Card, Suit}; use crate::card::Card;
use crate::pile::Pile; use crate::pile::Pile;
/// Returns `true` if `card` can be placed on `pile` as the next card in the foundation for `suit`. /// Returns `true` if `card` can be placed on the foundation `pile`.
/// ///
/// Foundation rules: same suit, Ace starts, each subsequent card is one rank higher. /// Foundation rules:
pub fn can_place_on_foundation(card: &Card, pile: &Pile, suit: Suit) -> bool { /// - When the pile is empty, any Ace is accepted; the placed Ace's suit
if card.suit != suit { /// becomes the pile's claimed suit (derived from the bottom card via
return false; /// [`Pile::claimed_suit`](crate::pile::Pile::claimed_suit)).
} /// - When the pile is non-empty, the next card must match the top card's
/// suit and be exactly one rank higher.
pub fn can_place_on_foundation(card: &Card, pile: &Pile) -> bool {
match pile.cards.last() { match pile.cards.last() {
None => card.rank.value() == 1, None => card.rank.value() == 1,
Some(top) => card.rank.value() == top.rank.value() + 1, Some(top) => card.suit == top.suit && card.rank.value() == top.rank.value() + 1,
} }
} }
@@ -45,37 +47,46 @@ mod tests {
// Foundation tests // Foundation tests
#[test] #[test]
fn foundation_ace_on_empty_is_valid() { fn foundation_ace_on_empty_is_valid() {
let c = card(Suit::Hearts, Rank::Ace); // Every suit's Ace must land on an empty foundation slot regardless of
let p = Pile::new(PileType::Foundation(Suit::Hearts)); // its slot index; the slot claims the suit only after the Ace lands.
assert!(can_place_on_foundation(&c, &p, Suit::Hearts)); for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
let c = card(suit, Rank::Ace);
let p = Pile::new(PileType::Foundation(0));
assert!(
can_place_on_foundation(&c, &p),
"Ace of {suit:?} must land on empty slot 0",
);
}
} }
#[test] #[test]
fn foundation_non_ace_on_empty_is_invalid() { fn foundation_non_ace_on_empty_is_invalid() {
let c = card(Suit::Hearts, Rank::Two); let c = card(Suit::Hearts, Rank::Two);
let p = Pile::new(PileType::Foundation(Suit::Hearts)); let p = Pile::new(PileType::Foundation(0));
assert!(!can_place_on_foundation(&c, &p, Suit::Hearts)); assert!(!can_place_on_foundation(&c, &p));
} }
#[test] #[test]
fn foundation_two_on_ace_same_suit_is_valid() { fn foundation_two_on_ace_same_suit_is_valid() {
let c = card(Suit::Clubs, Rank::Two); let c = card(Suit::Clubs, Rank::Two);
let p = pile_with(PileType::Foundation(Suit::Clubs), vec![card(Suit::Clubs, Rank::Ace)]); let p = pile_with(PileType::Foundation(0), vec![card(Suit::Clubs, Rank::Ace)]);
assert!(can_place_on_foundation(&c, &p, Suit::Clubs)); assert!(can_place_on_foundation(&c, &p));
} }
#[test] #[test]
fn foundation_wrong_suit_is_invalid() { fn foundation_second_card_must_match_claimed_suit() {
let c = card(Suit::Hearts, Rank::Ace); // Place Ace of Hearts on slot 0, then attempt 2 of Spades — rejected
let p = Pile::new(PileType::Foundation(Suit::Spades)); // because the slot's claimed suit is Hearts after the Ace lands.
assert!(!can_place_on_foundation(&c, &p, Suit::Spades)); let p = pile_with(PileType::Foundation(0), vec![card(Suit::Hearts, Rank::Ace)]);
let c = card(Suit::Spades, Rank::Two);
assert!(!can_place_on_foundation(&c, &p));
} }
#[test] #[test]
fn foundation_skipping_rank_is_invalid() { fn foundation_skipping_rank_is_invalid() {
let c = card(Suit::Diamonds, Rank::Three); let c = card(Suit::Diamonds, Rank::Three);
let p = pile_with(PileType::Foundation(Suit::Diamonds), vec![card(Suit::Diamonds, Rank::Ace)]); let p = pile_with(PileType::Foundation(0), vec![card(Suit::Diamonds, Rank::Ace)]);
assert!(!can_place_on_foundation(&c, &p, Suit::Diamonds)); assert!(!can_place_on_foundation(&c, &p));
} }
// Tableau tests // Tableau tests
@@ -125,16 +136,16 @@ mod tests {
fn foundation_king_on_queen_completes_suit() { fn foundation_king_on_queen_completes_suit() {
// The last card placed to complete a foundation is always King on Queen. // The last card placed to complete a foundation is always King on Queen.
let c = card(Suit::Spades, Rank::King); let c = card(Suit::Spades, Rank::King);
let p = pile_with(PileType::Foundation(Suit::Spades), vec![card(Suit::Spades, Rank::Queen)]); let p = pile_with(PileType::Foundation(0), vec![card(Suit::Spades, Rank::Queen)]);
assert!(can_place_on_foundation(&c, &p, Suit::Spades)); assert!(can_place_on_foundation(&c, &p));
} }
#[test] #[test]
fn foundation_king_wrong_suit_is_invalid() { fn foundation_king_wrong_suit_is_invalid() {
// King of Hearts cannot go on a Spades foundation even if rank matches. // King of Hearts cannot go on a Spades-claimed foundation even if rank matches.
let c = card(Suit::Hearts, Rank::King); let c = card(Suit::Hearts, Rank::King);
let p = pile_with(PileType::Foundation(Suit::Spades), vec![card(Suit::Spades, Rank::Queen)]); let p = pile_with(PileType::Foundation(0), vec![card(Suit::Spades, Rank::Queen)]);
assert!(!can_place_on_foundation(&c, &p, Suit::Spades)); assert!(!can_place_on_foundation(&c, &p));
} }
#[test] #[test]
+3 -4
View File
@@ -33,12 +33,11 @@ pub fn compute_time_bonus(elapsed_seconds: u64) -> i32 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::card::Suit;
#[test] #[test]
fn move_to_foundation_scores_ten() { fn move_to_foundation_scores_ten() {
assert_eq!(score_move(&PileType::Waste, &PileType::Foundation(Suit::Hearts)), 10); assert_eq!(score_move(&PileType::Waste, &PileType::Foundation(2)), 10);
assert_eq!(score_move(&PileType::Tableau(0), &PileType::Foundation(Suit::Clubs)), 10); assert_eq!(score_move(&PileType::Tableau(0), &PileType::Foundation(0)), 10);
} }
#[test] #[test]
@@ -74,7 +73,7 @@ mod tests {
#[test] #[test]
fn non_waste_to_tableau_scores_zero() { fn non_waste_to_tableau_scores_zero() {
// Foundation → Tableau is impossible in practice but must score 0. // Foundation → Tableau is impossible in practice but must score 0.
assert_eq!(score_move(&PileType::Foundation(Suit::Clubs), &PileType::Tableau(0)), 0); assert_eq!(score_move(&PileType::Foundation(0), &PileType::Tableau(0)), 0);
// Tableau → Tableau (restack) scores 0. // Tableau → Tableau (restack) scores 0.
assert_eq!(score_move(&PileType::Tableau(1), &PileType::Tableau(2)), 0); assert_eq!(score_move(&PileType::Tableau(1), &PileType::Tableau(2)), 0);
} }
+9
View File
@@ -16,3 +16,12 @@ dirs = { workspace = true }
keyring-core = { workspace = true } keyring-core = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
[dev-dependencies]
solitaire_server = { path = "../solitaire_server" }
solitaire_sync = { workspace = true }
axum = { workspace = true }
sqlx = { workspace = true }
jsonwebtoken = { workspace = true }
uuid = { workspace = true }
chrono = { workspace = true }
+3 -2
View File
@@ -40,8 +40,9 @@ const SERVICE: &str = "solitaire_quest_server";
fn map_keyring_err(err: keyring_core::Error, username: &str) -> TokenError { fn map_keyring_err(err: keyring_core::Error, username: &str) -> TokenError {
let msg = err.to_string(); let msg = err.to_string();
match err { match err {
keyring_core::Error::NoStorageAccess(_) => TokenError::KeychainUnavailable(msg), keyring_core::Error::NoStorageAccess(_) | keyring_core::Error::NoDefaultStore => {
keyring_core::Error::NoDefaultStore => TokenError::KeychainUnavailable(msg), TokenError::KeychainUnavailable(msg)
}
keyring_core::Error::NoEntry => TokenError::NotFound(username.to_string()), keyring_core::Error::NoEntry => TokenError::NotFound(username.to_string()),
_ => TokenError::Keyring(msg), _ => TokenError::Keyring(msg),
} }
+1 -1
View File
@@ -126,7 +126,7 @@ pub use challenge::{challenge_count, challenge_seed_for, CHALLENGE_SEEDS};
pub mod settings; pub mod settings;
pub use settings::{ pub use settings::{
load_settings_from, save_settings_to, settings_file_path, AnimSpeed, Settings, SyncBackend, load_settings_from, save_settings_to, settings_file_path, AnimSpeed, Settings, SyncBackend,
Theme, Theme, WindowGeometry,
}; };
pub mod auth_tokens; pub mod auth_tokens;
+100
View File
@@ -61,6 +61,25 @@ pub enum SyncBackend {
} }
/// Persisted window size (in logical pixels) and screen position
/// (top-left corner, in physical pixels) — restored on next launch.
///
/// Stored inside [`Settings::window_geometry`]. `None` on `Settings`
/// means "use platform defaults"; a populated value is written every
/// time the player resizes or moves the window so the next launch
/// reopens at the same geometry.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct WindowGeometry {
/// Logical width of the window in pixels.
pub width: u32,
/// Logical height of the window in pixels.
pub height: u32,
/// X coordinate of the window's top-left corner, in physical pixels.
pub x: i32,
/// Y coordinate of the window's top-left corner, in physical pixels.
pub y: i32,
}
/// Persistent user settings. /// Persistent user settings.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Settings { pub struct Settings {
@@ -98,6 +117,21 @@ pub struct Settings {
/// solely on colour. /// solely on colour.
#[serde(default)] #[serde(default)]
pub color_blind_mode: bool, pub color_blind_mode: bool,
/// Window size and screen position to restore on next launch. `None`
/// means "use platform defaults" — set on first run, then populated
/// as the player resizes / moves the window. Older `settings.json`
/// files written before this field existed deserialize cleanly to
/// `None` thanks to `#[serde(default)]`.
#[serde(default)]
pub window_geometry: Option<WindowGeometry>,
/// Identifier of the active card-art theme. Matches `meta.id` from
/// the theme's `theme.ron` manifest. `"default"` is the bundled
/// theme and is always present in the registry; user-supplied
/// themes register under their own ids when they're imported.
/// Older `settings.json` files default cleanly to `"default"` via
/// `#[serde(default = ...)]`.
#[serde(default = "default_theme_id")]
pub selected_theme_id: String,
} }
fn default_draw_mode() -> DrawMode { fn default_draw_mode() -> DrawMode {
@@ -112,6 +146,10 @@ fn default_music_volume() -> f32 {
0.5 0.5
} }
fn default_theme_id() -> String {
"default".to_string()
}
impl Default for Settings { impl Default for Settings {
fn default() -> Self { fn default() -> Self {
Self { Self {
@@ -125,6 +163,8 @@ impl Default for Settings {
selected_background: 0, selected_background: 0,
first_run_complete: false, first_run_complete: false,
color_blind_mode: false, color_blind_mode: false,
window_geometry: None,
selected_theme_id: default_theme_id(),
} }
} }
} }
@@ -276,6 +316,8 @@ mod tests {
selected_background: 0, selected_background: 0,
first_run_complete: true, first_run_complete: true,
color_blind_mode: false, color_blind_mode: false,
window_geometry: None,
selected_theme_id: "default".to_string(),
}; };
save_settings_to(&path, &s).expect("save"); save_settings_to(&path, &s).expect("save");
let loaded = load_settings_from(&path); let loaded = load_settings_from(&path);
@@ -406,4 +448,62 @@ mod tests {
assert_eq!(loaded.selected_background, 3, "selected_background must survive serde round-trip"); assert_eq!(loaded.selected_background, 3, "selected_background must survive serde round-trip");
let _ = fs::remove_file(&path); let _ = fs::remove_file(&path);
} }
// -----------------------------------------------------------------------
// window_geometry — persisted window size/position
// -----------------------------------------------------------------------
#[test]
fn settings_window_geometry_default_is_none() {
assert!(
Settings::default().window_geometry.is_none(),
"default window_geometry must be None so first launch uses platform defaults"
);
}
#[test]
fn settings_with_window_geometry_round_trip() {
let path = tmp_path("window_geometry_round_trip");
let _ = fs::remove_file(&path);
let geom = WindowGeometry {
width: 1440,
height: 900,
x: 120,
y: 80,
};
let s = Settings {
window_geometry: Some(geom),
..Settings::default()
};
save_settings_to(&path, &s).expect("save");
let loaded = load_settings_from(&path);
assert_eq!(
loaded.window_geometry,
Some(geom),
"window_geometry must survive serde round-trip"
);
let _ = fs::remove_file(&path);
}
#[test]
fn legacy_settings_without_window_geometry_deserializes_to_none() {
// A settings.json written by an older version of the game will be
// missing this field entirely. `#[serde(default)]` on the field
// must yield `None` rather than failing the whole deserialise.
let json = br#"{ "sfx_volume": 0.7, "first_run_complete": true }"#;
let s: Settings = serde_json::from_slice(json).unwrap_or_default();
assert!(
s.window_geometry.is_none(),
"legacy settings.json missing window_geometry must deserialize to None"
);
}
#[test]
fn window_geometry_explicit_null_deserializes_to_none() {
// An explicit `"window_geometry": null` is also valid input that
// must yield None — keeps tooling that hand-edits the file safe.
let json = br#"{ "window_geometry": null }"#;
let s: Settings = serde_json::from_slice(json).unwrap_or_default();
assert!(s.window_geometry.is_none());
}
} }
+59 -4
View File
@@ -7,7 +7,7 @@ use std::fs;
use std::io; use std::io;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use solitaire_core::game_state::GameState; use solitaire_core::game_state::{GameState, GAME_STATE_SCHEMA_VERSION};
use crate::stats::StatsSnapshot; use crate::stats::StatsSnapshot;
@@ -72,10 +72,21 @@ pub fn game_state_file_path() -> Option<PathBuf> {
} }
/// Load an in-progress `GameState` from `path`. Returns `None` if the file is /// Load an in-progress `GameState` from `path`. Returns `None` if the file is
/// missing, corrupt, or represents a finished game. /// missing, corrupt, represents a finished game, or carries a save-schema
/// version other than [`GAME_STATE_SCHEMA_VERSION`].
///
/// Schema mismatch is treated as "no save" so a player upgrading across an
/// incompatible game-state format change starts fresh instead of seeing a
/// half-loaded game (or a deserialiser error). v1 saves with the old
/// `Foundation(Suit)` key shape will fail to parse outright; any v1 saves
/// that happen to round-trip but report `schema_version: 1` are also rejected
/// here.
pub fn load_game_state_from(path: &Path) -> Option<GameState> { pub fn load_game_state_from(path: &Path) -> Option<GameState> {
let data = fs::read(path).ok()?; let data = fs::read(path).ok()?;
let gs: GameState = serde_json::from_slice(&data).ok()?; let gs: GameState = serde_json::from_slice(&data).ok()?;
if gs.schema_version != GAME_STATE_SCHEMA_VERSION {
return None;
}
if gs.is_won { if gs.is_won {
None None
} else { } else {
@@ -138,8 +149,7 @@ fn cleanup_tmp_files_in(dir: &Path) {
if path if path
.file_name() .file_name()
.and_then(|n| n.to_str()) .and_then(|n| n.to_str())
.map(|n| n.ends_with(".json.tmp")) .is_some_and(|n| n.ends_with(".json.tmp"))
.unwrap_or(false)
{ {
let _ = fs::remove_file(&path); let _ = fs::remove_file(&path);
} }
@@ -332,4 +342,49 @@ mod tests {
let tmp = path.with_extension("json.tmp"); let tmp = path.with_extension("json.tmp");
assert!(!tmp.exists(), ".tmp must be cleaned up after rename"); assert!(!tmp.exists(), ".tmp must be cleaned up after rename");
} }
/// Pre-v2 save files used `Foundation(Suit)` keys and either fail to
/// parse outright or surface a `schema_version: 1`. Either path must
/// produce `None` so the player launches into a fresh game.
///
/// Sibling assertion: the stats round-trip path is unaffected — only
/// the game-state schema bumped.
#[test]
fn save_format_v1_is_rejected() {
let path = gs_path("schema_v1");
let _ = fs::remove_file(&path);
// A pared-down v1 JSON literal: foundation pile keys use the old
// suit-tagged form and the file omits `schema_version` (so it
// deserialises with the default of 1). Even if a future change
// makes `Foundation(Suit)` parse-compatible, the schema-version
// gate keeps this case rejected.
let v1_json = r#"{
"piles": [
[{"Foundation": "Hearts"}, {"pile_type": {"Foundation": "Hearts"}, "cards": []}]
],
"draw_mode": "DrawOne",
"score": 0,
"move_count": 0,
"elapsed_seconds": 0,
"seed": 42,
"is_won": false,
"is_auto_completable": false,
"undo_count": 0,
"undo_stack": []
}"#;
fs::write(&path, v1_json).expect("write v1 fixture");
assert!(
load_game_state_from(&path).is_none(),
"v1 game_state.json must be rejected (parse failure or schema bump)",
);
// Sibling sanity: stats files are independent and still round-trip.
let stats_path = tmp_path("schema_unrelated_stats");
let _ = fs::remove_file(&stats_path);
save_stats_to(&stats_path, &StatsSnapshot::default()).expect("save stats");
let loaded = load_stats_from(&stats_path);
assert_eq!(loaded, StatsSnapshot::default());
}
} }
+420
View File
@@ -0,0 +1,420 @@
//! Client-side sync round-trip integration tests for `solitaire_data`.
//!
//! These tests spin up the actual `solitaire_server` Axum app in-process on a
//! random TCP port (allocated by the OS) and drive the production
//! [`SolitaireServerClient`] HTTP client against it via `reqwest`. They are
//! the client-side counterpart to `solitaire_server/tests/server_tests.rs`,
//! which exercises the server endpoints directly via `tower::ServiceExt`.
//!
//! # Keyring
//!
//! [`SolitaireServerClient`] reads tokens from the OS keyring via
//! `keyring_core`. Headless test environments may not have a real secret
//! service, so we install the in-memory `keyring_core::mock::Store` exactly
//! once via [`std::sync::Once`]. Every test uses a unique username so the
//! shared mock store does not leak credentials between tests.
//!
//! # Server harness
//!
//! Each test calls [`spawn_test_server`] which:
//! 1. Binds a `tokio::net::TcpListener` on `127.0.0.1:0` (OS picks a port).
//! 2. Builds the in-memory SQLite pool, runs migrations.
//! 3. Builds the test router via `solitaire_server::build_test_router`
//! (rate limiting OFF, fixed test JWT secret).
//! 4. Spawns the server in a background `tokio::spawn` task.
//! 5. Returns the server URL (`http://127.0.0.1:{port}`).
//!
//! # Test JWT secret
//!
//! Must match the constant inside `build_test_router` so we can craft
//! expired-on-purpose tokens for the JWT-refresh test.
use chrono::Utc;
use jsonwebtoken::{encode, EncodingKey, Header};
use solitaire_data::{
delete_tokens, store_tokens, SolitaireServerClient, SyncError, SyncProvider,
};
use solitaire_sync::{PlayerProgress, StatsSnapshot, SyncPayload};
use sqlx::sqlite::SqlitePoolOptions;
use sqlx::SqlitePool;
use std::sync::Once;
use uuid::Uuid;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
/// JWT secret used by `solitaire_server::build_test_router`. Must stay in
/// sync with the constant inside that function.
const TEST_SECRET: &str = "test_secret_32_chars_minimum_ok!";
// ---------------------------------------------------------------------------
// Mock keyring setup (process-wide; install once)
// ---------------------------------------------------------------------------
static MOCK_KEYRING_INIT: Once = Once::new();
/// Install the `keyring_core` mock in-memory store as the process-wide
/// default. Safe to call from any test — only the first call has effect.
fn ensure_mock_keyring() {
MOCK_KEYRING_INIT.call_once(|| {
let store = keyring_core::mock::Store::new()
.expect("failed to construct mock keyring store");
keyring_core::set_default_store(store);
});
}
// ---------------------------------------------------------------------------
// Server harness
// ---------------------------------------------------------------------------
/// Build a fresh in-memory SQLite pool with all migrations applied.
///
/// `max_connections(1)` is required: each connection to `sqlite::memory:` is
/// a *separate* database, so a larger pool sees an empty schema on the second
/// borrow. Mirrors the pattern in `solitaire_server/tests/server_tests.rs`.
async fn fresh_pool() -> SqlitePool {
let pool = SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("failed to connect to in-memory SQLite database");
sqlx::migrate!("../solitaire_server/migrations")
.run(&pool)
.await
.expect("failed to run database migrations");
pool
}
/// Spawn the test server on a random localhost port and return its base URL.
///
/// The server runs until the test process exits — there is no explicit
/// shutdown. This is acceptable for `cargo test` where each test binary is a
/// separate process.
async fn spawn_test_server() -> String {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("failed to bind test listener");
let addr = listener
.local_addr()
.expect("listener has no local addr");
let app = solitaire_server::build_test_router(fresh_pool().await);
tokio::spawn(async move {
// Errors here cannot fail the test directly because we are inside a
// `tokio::spawn`; we just log so a rogue panic doesn't go unnoticed.
if let Err(e) = axum::serve(listener, app).await {
eprintln!("test server crashed: {e}");
}
});
format!("http://{addr}")
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/// Register a fresh user against `base_url` and return the access + refresh
/// tokens straight from the response body. Bypasses the keyring entirely so
/// the caller can store the tokens under whatever username they want.
async fn register_user_raw(
base_url: &str,
username: &str,
password: &str,
) -> (String, String) {
let client = reqwest::Client::new();
let resp = client
.post(format!("{base_url}/api/auth/register"))
.json(&serde_json::json!({
"username": username,
"password": password,
}))
.send()
.await
.expect("register request failed");
assert!(
resp.status().is_success(),
"register must succeed (got {})",
resp.status()
);
let body: serde_json::Value = resp.json().await.expect("register body must be JSON");
let access = body["access_token"]
.as_str()
.expect("access_token missing")
.to_string();
let refresh = body["refresh_token"]
.as_str()
.expect("refresh_token missing")
.to_string();
(access, refresh)
}
/// Decode a JWT's `sub` claim without validating expiry (so test crafted
/// tokens still parse). Returns the user UUID as a `String`.
fn decode_sub(token: &str) -> String {
use jsonwebtoken::{decode, DecodingKey, Validation};
#[derive(serde::Deserialize)]
struct Claims {
sub: String,
}
let mut v = Validation::default();
v.validate_exp = false;
let data = decode::<Claims>(
token,
&DecodingKey::from_secret(TEST_SECRET.as_bytes()),
&v,
)
.expect("failed to decode JWT");
data.claims.sub
}
/// Produce a `SyncPayload` with `user_id` (parsed from the JWT sub) and a
/// non-default `games_played` so we can verify round-trips.
fn make_payload(user_id_str: &str, games_played: u32) -> SyncPayload {
SyncPayload {
user_id: Uuid::parse_str(user_id_str)
.expect("user_id_str from JWT sub must be a valid UUID"),
stats: StatsSnapshot {
games_played,
games_won: 7,
best_single_score: 1234,
..StatsSnapshot::default()
},
achievements: vec![],
progress: PlayerProgress::default(),
last_modified: Utc::now(),
}
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
/// **Full happy-path round-trip.**
///
/// 1. Spin up server.
/// 2. Register a user via raw HTTP.
/// 3. Persist the tokens in the (mock) keyring under the same username.
/// 4. Construct a `SolitaireServerClient` and call `push()` with a known
/// payload, then call `pull()` on the *same* client.
/// 5. Assert the server-merged stats reflect the values we pushed.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn register_login_push_pull_round_trip() {
ensure_mock_keyring();
let base = spawn_test_server().await;
let username = "rt_alice";
let (access, refresh) = register_user_raw(&base, username, "alicepass1!").await;
store_tokens(username, &access, &refresh)
.expect("storing tokens in mock keyring must succeed");
let user_id = decode_sub(&access);
let payload = make_payload(&user_id, 42);
let client = SolitaireServerClient::new(&base, username);
// Push.
let push_resp = client
.push(&payload)
.await
.expect("push must succeed for an authenticated client");
assert_eq!(
push_resp.merged.stats.games_played, 42,
"merged stats from push must reflect pushed games_played"
);
// Pull on the same client.
let pulled = client
.pull()
.await
.expect("pull must succeed for an authenticated client");
assert_eq!(
pulled.stats.games_played, 42,
"pulled games_played must match what we pushed"
);
assert_eq!(
pulled.stats.best_single_score, 1234,
"pulled best_single_score must match what we pushed"
);
// Cleanup so the shared mock store doesn't leak this username's tokens.
let _ = delete_tokens(username);
}
/// **Concurrent two-client merge.**
///
/// Two clients (same user) push payloads with different `games_played`. The
/// server's merge keeps the higher of the two values. A subsequent pull from
/// either client must observe the merged max.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn pull_after_concurrent_pushes_merges_correctly() {
ensure_mock_keyring();
let base = spawn_test_server().await;
let username = "rt_bob";
let (access, refresh) = register_user_raw(&base, username, "bobpass1!").await;
store_tokens(username, &access, &refresh)
.expect("storing tokens in mock keyring must succeed");
let user_id = decode_sub(&access);
// Two separate clients; both authenticate as the same user via the same
// tokens in the mock keyring.
let client_a = SolitaireServerClient::new(&base, username);
let client_b = SolitaireServerClient::new(&base, username);
// Client A: low value first.
let payload_a = make_payload(&user_id, 5);
client_a.push(&payload_a).await.expect("client A push must succeed");
// Client B: higher value second.
let payload_b = make_payload(&user_id, 99);
client_b.push(&payload_b).await.expect("client B push must succeed");
// Either client should now pull max(5, 99) = 99.
let pulled = client_a
.pull()
.await
.expect("pull after concurrent pushes must succeed");
assert_eq!(
pulled.stats.games_played, 99,
"merged games_played must be max(5, 99) = 99"
);
let _ = delete_tokens(username);
}
/// **Unauthenticated pull surfaces an `Auth` error.**
///
/// We construct a client for a user who has *no* tokens in the keyring at
/// all. `pull()` must return `SyncError::Auth(_)` — never `Network` or
/// `Serialization`.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn unauthenticated_pull_returns_authentication_error() {
ensure_mock_keyring();
let base = spawn_test_server().await;
// Use a username that we never call `store_tokens` for so the keyring
// lookup fails before any HTTP request is made.
let username = "rt_no_creds";
// Defensive: in case a previous test run left tokens behind.
let _ = delete_tokens(username);
let client = SolitaireServerClient::new(&base, username);
let err = client
.pull()
.await
.expect_err("pull must fail without stored credentials");
assert!(
matches!(err, SyncError::Auth(_)),
"expected SyncError::Auth, got {err:?}"
);
}
/// **JWT auto-refresh on 401.**
///
/// We register a user, then deliberately overwrite the stored access token
/// with one whose `exp` is in the past (signed with the same `TEST_SECRET`
/// so the signature verifies). The middleware will reject it with 401, the
/// `SolitaireServerClient` should call `/api/auth/refresh` with the still-
/// valid refresh token and retry — and `pull()` must ultimately succeed.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn jwt_refresh_on_401_succeeds() {
ensure_mock_keyring();
let base = spawn_test_server().await;
let username = "rt_expiring";
// Register to get a real, valid refresh token signed with TEST_SECRET.
let (_real_access, real_refresh) =
register_user_raw(&base, username, "expirepass1!").await;
let user_id = decode_sub(&_real_access);
// Craft an expired access token signed with TEST_SECRET so the server's
// signature check still passes but the expiry validation rejects it.
#[derive(serde::Serialize)]
struct Claims {
sub: String,
exp: usize,
kind: String,
}
let exp = (Utc::now() - chrono::Duration::hours(2)).timestamp() as usize;
let expired_access = encode(
&Header::default(),
&Claims {
sub: user_id.clone(),
exp,
kind: "access".into(),
},
&EncodingKey::from_secret(TEST_SECRET.as_bytes()),
)
.expect("failed to encode expired access token");
// Overwrite the stored access token with the expired one. The refresh
// token stays valid so the client's refresh path can succeed.
store_tokens(username, &expired_access, &real_refresh)
.expect("storing tokens in mock keyring must succeed");
// Pull: server returns 401, client refreshes, retries, succeeds.
let client = SolitaireServerClient::new(&base, username);
let pulled = client.pull().await.expect(
"pull must succeed after the client transparently refreshes the access token",
);
// Default merge for a never-pushed user yields games_played = 0.
assert_eq!(
pulled.stats.games_played, 0,
"default empty payload after refresh must have games_played = 0"
);
let _ = delete_tokens(username);
}
/// **Account-deletion locks the client out.**
///
/// Register, push some data, then delete the account via the trait method.
/// A subsequent push with the *same* tokens (still cryptographically valid —
/// the server has no revocation list) must surface a non-success response
/// because the user row is gone and the server rejects the foreign-key push.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn pull_after_account_deletion_returns_default_or_error() {
ensure_mock_keyring();
let base = spawn_test_server().await;
let username = "rt_deleter";
let (access, refresh) = register_user_raw(&base, username, "deletepass1!").await;
store_tokens(username, &access, &refresh)
.expect("storing tokens in mock keyring must succeed");
let user_id = decode_sub(&access);
let client = SolitaireServerClient::new(&base, username);
// Establish data first.
client
.push(&make_payload(&user_id, 3))
.await
.expect("initial push must succeed");
// Delete the account.
client
.delete_account()
.await
.expect("delete_account must return Ok on the live server");
// After deletion, pushing the same payload may either:
// - succeed (server INSERTs a fresh sync_state row keyed off JWT sub
// even though the users row is gone), or
// - fail with a server error from a foreign-key violation.
//
// We do not pin down which behaviour the server picks — the contract we
// assert is just that the client surfaces *some* result without panicking
// and that the trait remains usable.
let post_delete_push = client.push(&make_payload(&user_id, 4)).await;
let _ = post_delete_push; // either Ok or Err is fine; no panic is the win
let _ = delete_tokens(username);
}
+9
View File
@@ -13,6 +13,15 @@ solitaire_sync = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
uuid = { workspace = true } uuid = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }
usvg = { workspace = true }
resvg = { workspace = true }
tiny-skia = { workspace = true }
ron = { workspace = true }
dirs = { workspace = true }
zip = { workspace = true }
[dev-dependencies] [dev-dependencies]
async-trait = { workspace = true } async-trait = { workspace = true }
tempfile = { workspace = true }
@@ -0,0 +1,43 @@
# Default theme — provenance
This directory is the bundled-default card theme that ships embedded in
the binary via Bevy's `embedded_asset!` macro (see
`solitaire_engine/src/assets/sources.rs`). At runtime its files are
addressable as `embedded://solitaire_engine/assets/themes/default/...`.
## Current state (Phase 3)
The `theme.ron` manifest in this directory lists all 52 face slots plus
a back slot, but **the referenced SVG files do not yet exist**. The
manifest is intentionally a stub so that:
1. `embedded_asset!` has a real file to bundle (the manifest itself).
2. `ThemeManifest::validate` accepts the manifest (it requires all 52
faces to be listed by name).
3. The `embedded://` asset source can be source-registered and queried
without runtime errors during Phase 3.
The actual SVG art will be added when the project swaps in the
`hayeah/playing-cards-assets` artwork — see the implementation plan in
`/CARD_PLAN.md`. At that point, every `.svg` filename listed in
`theme.ron`'s `faces` map (and `back.svg`) must be added here, and each
new file needs a corresponding `embedded_asset!(app, ...)` call in
`solitaire_engine/src/assets/sources.rs::register_default_theme`.
## How to add files to the bundled default theme
For each new file you drop into this directory:
1. Drop the file under `solitaire_engine/assets/themes/default/`.
2. Add one line to `register_default_theme` in
`solitaire_engine/src/assets/sources.rs` of the form:
```rust
embedded_asset!(app, "../../assets/themes/default/<filename>");
```
(The path is relative to `sources.rs`, which lives in
`solitaire_engine/src/assets/`.)
3. Update this file with the licence and origin of the new asset.
## Licence
To be filled in once real artwork lands.
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Default theme card back — Solitaire Quest's midnight-purple palette.
Original work, MIT-licensed alongside the rest of this project.
Aspect 2:3 to match the face SVGs from hayeah/playing-cards-assets.
-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 300" width="200" height="300">
<defs>
<pattern id="diamonds" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="20" height="20" fill="#1A0F2E"/>
<path d="M 10 0 L 20 10 L 10 20 L 0 10 Z"
fill="none" stroke="#3A2580" stroke-width="1"/>
<circle cx="10" cy="10" r="1" fill="#FFD23F"/>
</pattern>
</defs>
<!-- Outer card surface with a midnight-purple base + diamond lattice -->
<rect x="0" y="0" width="200" height="300" rx="12" ry="12" fill="#1A0F2E"/>
<rect x="6" y="6" width="188" height="288" rx="9" ry="9" fill="url(#diamonds)"/>
<!-- Bordered inset so the lattice has a clear edge -->
<rect x="14" y="14" width="172" height="272" rx="6" ry="6"
fill="none" stroke="#FFD23F" stroke-width="1.5" opacity="0.85"/>
<!-- Centred diamond medallion -->
<g transform="translate(100 150)">
<path d="M 0 -42 L 42 0 L 0 42 L -42 0 Z" fill="#2D1B69" stroke="#FFD23F" stroke-width="2"/>
<path d="M 0 -22 L 22 0 L 0 22 L -22 0 Z" fill="#3A2580" stroke="#FFD23F" stroke-width="1"/>
<circle cx="0" cy="0" r="4" fill="#FFD23F"/>
</g>
<!-- Corner pips picking up the magenta secondary accent so the back
still reads as part of the design system at a glance -->
<g fill="#FF6B9D">
<circle cx="22" cy="22" r="2.5"/>
<circle cx="178" cy="22" r="2.5"/>
<circle cx="22" cy="278" r="2.5"/>
<circle cx="178" cy="278" r="2.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1,281 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="10_of_clubs.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/10_of_clubs.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3760"
cx="48.231091"
cy="18.137882"
fx="48.231091"
fy="18.137882"
r="9.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
id="linearGradient2984"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#000000;stop-opacity:0.65648854;"
offset="1"
id="stop2988" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784"
id="radialGradient3792"
cx="171.48665"
cy="511.22299"
fx="171.48665"
fy="511.22299"
r="81.902771"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3784"><stop
style="stop-color:#ffffff;stop-opacity:0.53435117;"
offset="0"
id="stop3786" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3855"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.51908398;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
r="81.902771"
fy="461.84113"
fx="181.69392"
cy="461.84113"
cx="181.69392"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3916"
xlink:href="#linearGradient3784-3"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-3"><stop
style="stop-color:#ffffff;stop-opacity:0.70229006;"
offset="0"
id="stop3786-86" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="2.4336873"
inkscape:cx="117.62976"
inkscape:cy="148.16686"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<g
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
id="layer1-1-4"><path
id="cl-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g>
<g
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
id="layer1-1-4-1"><path
id="cl-9-7"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 57.572834,25.099947 c 0,0 5.967372,-4.773898 5.967372,-11.392027 0,-3.8743954 -3.43972,-10.3065945 -11.392028,-10.3065945 -7.952308,0 -11.392028,6.4347116 -11.392028,10.3065945 0,6.618129 5.967373,11.392027 5.967373,11.392027 -6.62818,-5.163348 -18.444833,-1.638201 -18.444833,8.680956 0,5.16586 4.22113,10.849311 10.849311,10.849311 7.952308,0 11.392027,-8.680956 11.392027,-8.680956 0,0 1.010056,9.894531 -4.881939,15.191045 h 13.020178 c -5.891994,-5.294001 -4.881938,-15.191045 -4.881938,-15.191045 0,0 3.439718,8.680956 11.392027,8.680956 6.630693,0 10.849311,-5.685963 10.849311,-10.849311 0,-10.319157 -11.816654,-13.844304 -18.444833,-8.680956 z"
id="cl-9-8" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 57.110434,93.200747 c 0,0 5.967372,-4.773898 5.967372,-11.392027 0,-3.874396 -3.43972,-10.306594 -11.392028,-10.306594 -7.952308,0 -11.392028,6.434711 -11.392028,10.306594 0,6.618129 5.967373,11.392027 5.967373,11.392027 -6.62818,-5.163348 -18.444833,-1.638201 -18.444833,8.680953 0,5.16587 4.22113,10.84932 10.849311,10.84932 7.952308,0 11.392027,-8.68096 11.392027,-8.68096 0,0 1.010056,9.89453 -4.881939,15.19104 h 13.020178 c -5.891994,-5.294 -4.881938,-15.19104 -4.881938,-15.19104 0,0 3.439718,8.68096 11.392027,8.68096 6.630693,0 10.849311,-5.68597 10.849311,-10.84932 0,-10.319154 -11.816654,-13.844301 -18.444833,-8.680953 z"
id="cl-9-8-0" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 121.55789,24.926219 c 0,0 5.96737,-4.773898 5.96737,-11.392027 0,-3.8743954 -3.43971,-10.3065945 -11.39203,-10.3065945 -7.95231,0 -11.39202,6.4347116 -11.39202,10.3065945 0,6.618129 5.96737,11.392027 5.96737,11.392027 -6.62818,-5.163348 -18.444834,-1.638201 -18.444834,8.680956 0,5.16586 4.22113,10.849311 10.849304,10.849311 7.95231,0 11.39203,-8.680956 11.39203,-8.680956 0,0 1.01006,9.894531 -4.88193,15.191045 h 13.02017 c -5.89199,-5.294001 -4.88193,-15.191045 -4.88193,-15.191045 0,0 3.43971,8.680956 11.39202,8.680956 6.63069,0 10.84931,-5.685963 10.84931,-10.849311 0,-10.319157 -11.81665,-13.844304 -18.44483,-8.680956 z"
id="cl-9-8-9" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 121.55789,93.027019 c 0,0 5.96737,-4.773898 5.96737,-11.392028 0,-3.874395 -3.43971,-10.306593 -11.39203,-10.306593 -7.95231,0 -11.39202,6.434711 -11.39202,10.306593 0,6.61813 5.96737,11.392028 5.96737,11.392028 -6.62818,-5.163348 -18.444834,-1.638201 -18.444834,8.680951 0,5.16587 4.22113,10.84932 10.849304,10.84932 7.95231,0 11.39203,-8.68096 11.39203,-8.68096 0,0 1.01006,9.89453 -4.88193,15.19104 h 13.02017 c -5.89199,-5.294 -4.88193,-15.19104 -4.88193,-15.19104 0,0 3.43971,8.68096 11.39202,8.68096 6.63069,0 10.84931,-5.68597 10.84931,-10.84932 0,-10.319152 -11.81665,-13.844299 -18.44483,-8.680951 z"
id="cl-9-8-0-4" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 89.576798,59.281103 c 0,0 5.967372,-4.773897 5.967372,-11.392027 0,-3.874395 -3.43972,-10.306594 -11.392028,-10.306594 -7.952308,0 -11.392028,6.434712 -11.392028,10.306594 0,6.61813 5.967373,11.392027 5.967373,11.392027 -6.62818,-5.163347 -18.444833,-1.638201 -18.444833,8.680957 0,5.165859 4.22113,10.84931 10.849311,10.84931 7.952308,0 11.392027,-8.680956 11.392027,-8.680956 0,0 1.010056,9.894531 -4.881939,15.191045 h 13.020178 c -5.891994,-5.294001 -4.881938,-15.191045 -4.881938,-15.191045 0,0 3.439718,8.680956 11.392027,8.680956 6.63069,0 10.84931,-5.685963 10.84931,-10.84931 0,-10.319158 -11.816653,-13.844304 -18.444832,-8.680957 z"
id="cl-9-8-8" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 110.06258,217.80216 c 0,0 -5.96737,4.77391 -5.96737,11.39203 0,3.8744 3.43971,10.3066 11.39202,10.3066 7.95232,0 11.39203,-6.43471 11.39203,-10.3066 0,-6.61812 -5.96737,-11.39203 -5.96737,-11.39203 6.62818,5.16335 18.44483,1.6382 18.44483,-8.68095 0,-5.16586 -4.22112,-10.84931 -10.84931,-10.84931 -7.95231,0 -11.39202,8.68095 -11.39202,8.68095 0,0 -1.01006,-9.89453 4.88193,-15.19104 h -13.02017 c 5.89199,5.294 4.88193,15.19104 4.88193,15.19104 0,0 -3.43972,-8.68095 -11.39203,-8.68095 -6.630687,0 -10.849305,5.68596 -10.849305,10.84931 0,10.31915 11.816655,13.8443 18.444835,8.68095 z"
id="cl-9-8-4" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 110.70832,149.70136 c 0,0 -5.96737,4.77391 -5.96737,11.39203 0,3.8744 3.43971,10.3066 11.39202,10.3066 7.95232,0 11.39203,-6.43471 11.39203,-10.3066 0,-6.61812 -5.96737,-11.39203 -5.96737,-11.39203 6.62818,5.16335 18.44483,1.6382 18.44483,-8.68095 0,-5.16586 -4.22112,-10.84931 -10.84931,-10.84931 -7.95231,0 -11.39202,8.68095 -11.39202,8.68095 0,0 -1.01006,-9.89453 4.88193,-15.19104 h -13.02017 c 5.89199,5.294 4.88193,15.19104 4.88193,15.19104 0,0 -3.43972,-8.68095 -11.39203,-8.68095 -6.630687,0 -10.849305,5.68596 -10.849305,10.84931 0,10.31915 11.816655,13.8443 18.444835,8.68095 z"
id="cl-9-8-0-2" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 46.077633,217.97556 c 0,0 -5.967372,4.77391 -5.967372,11.39203 0,3.8744 3.43972,10.3066 11.392028,10.3066 7.952308,0 11.392028,-6.43471 11.392028,-10.3066 0,-6.61812 -5.967373,-11.39203 -5.967373,-11.39203 6.62818,5.16335 18.444833,1.6382 18.444833,-8.68095 0,-5.16586 -4.22113,-10.84931 -10.849311,-10.84931 -7.952308,0 -11.392027,8.68095 -11.392027,8.68095 0,0 -1.010056,-9.89453 4.881939,-15.19104 H 44.9922 c 5.891994,5.294 4.881938,15.19104 4.881938,15.19104 0,0 -3.439718,-8.68095 -11.392027,-8.68095 -6.630693,0 -10.849311,5.68596 -10.849311,10.84931 0,10.31915 11.816654,13.8443 18.444833,8.68095 z"
id="cl-9-8-9-6" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 46.261118,149.87509 c 0,0 -5.967372,4.77391 -5.967372,11.39203 0,3.8744 3.43972,10.3066 11.392028,10.3066 7.952308,0 11.392028,-6.43471 11.392028,-10.3066 0,-6.61812 -5.967373,-11.39203 -5.967373,-11.39203 6.62818,5.16335 18.444833,1.6382 18.444833,-8.68095 0,-5.16586 -4.22113,-10.84931 -10.849311,-10.84931 -7.952308,0 -11.392027,8.68095 -11.392027,8.68095 0,0 -1.010056,-9.89453 4.881939,-15.19104 H 45.175685 c 5.891994,5.294 4.881938,15.19104 4.881938,15.19104 0,0 -3.439718,-8.68095 -11.392027,-8.68095 -6.630693,0 -10.849311,5.68596 -10.849311,10.84931 0,10.31915 11.816654,13.8443 18.444833,8.68095 z"
id="cl-9-8-0-4-9" /><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-1.1621548"
y="27.170401"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="-1.1621548"
y="27.170401"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="11.000458"
y="27.499109"
id="text3038"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3040"
x="11.000458"
y="27.499109">0</tspan></text>
<path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 78.0698,183.9376 c 0,0 -5.96738,4.77389 -5.96738,11.39202 0,3.8744 3.43972,10.3066 11.39203,10.3066 7.95231,0 11.39203,-6.43471 11.39203,-10.3066 0,-6.61813 -5.96737,-11.39202 -5.96737,-11.39202 6.62818,5.16334 18.44483,1.6382 18.44483,-8.68096 0,-5.16586 -4.22113,-10.84931 -10.84931,-10.84931 -7.95231,0 -11.39203,8.68096 -11.39203,8.68096 0,0 -1.01005,-9.89454 4.88194,-15.19105 H 76.98436 c 5.892,5.294 4.88194,15.19105 4.88194,15.19105 0,0 -3.43972,-8.68096 -11.39203,-8.68096 -6.630688,0 -10.849308,5.68596 -10.849308,10.84931 0,10.31916 11.816658,13.8443 18.444838,8.68096 z"
id="cl-9-8-8-8" /><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-168.80901"
y="-216.22618"
id="text3788-0"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-6"
x="-168.80901"
y="-216.22618"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-156.64639"
y="-215.89748"
id="text3038-8"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3040-9"
x="-156.64639"
y="-215.89748">0</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,216 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="2_of_clubs.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/2_of_clubs.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3760"
cx="48.231091"
cy="18.137882"
fx="48.231091"
fy="18.137882"
r="9.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
id="linearGradient2984"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#000000;stop-opacity:0.65648854;"
offset="1"
id="stop2988" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784"
id="radialGradient3792"
cx="171.48665"
cy="511.22299"
fx="171.48665"
fy="511.22299"
r="81.902771"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3784"><stop
style="stop-color:#ffffff;stop-opacity:0.53435117;"
offset="0"
id="stop3786" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3855"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.51908398;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
r="81.902771"
fy="461.84113"
fx="181.69392"
cy="461.84113"
cx="181.69392"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3916"
xlink:href="#linearGradient3784-3"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-3"><stop
style="stop-color:#ffffff;stop-opacity:0.70229006;"
offset="0"
id="stop3786-86" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="2.4336873"
inkscape:cx="117.62976"
inkscape:cy="148.16686"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="8.3105459"
y="27.548409"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="8.3105459"
y="27.548409"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
<g
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
id="layer1-1-4"><path
id="cl-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-158.86395"
y="-214.4666"
id="text3788-8"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-7"
x="-158.86395"
y="-214.4666"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
<g
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
id="layer1-1-4-1"><path
id="cl-9-7"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-36.788386,-1.5311156)"
id="layer1-1-4-8"><path
id="cl-9-8"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,205.12954,245.27515)"
id="layer1-1-4-8-0"><path
id="cl-9-8-6"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g></svg>

After

Width:  |  Height:  |  Size: 8.4 KiB

@@ -0,0 +1,224 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="3_of_clubs.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/3_of_clubs.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3760"
cx="48.231091"
cy="18.137882"
fx="48.231091"
fy="18.137882"
r="9.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
id="linearGradient2984"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#000000;stop-opacity:0.65648854;"
offset="1"
id="stop2988" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784"
id="radialGradient3792"
cx="171.48665"
cy="511.22299"
fx="171.48665"
fy="511.22299"
r="81.902771"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3784"><stop
style="stop-color:#ffffff;stop-opacity:0.53435117;"
offset="0"
id="stop3786" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3855"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.51908398;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
r="81.902771"
fy="461.84113"
fx="181.69392"
cy="461.84113"
cx="181.69392"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3916"
xlink:href="#linearGradient3784-3"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-3"><stop
style="stop-color:#ffffff;stop-opacity:0.70229006;"
offset="0"
id="stop3786-86" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="2.4336873"
inkscape:cx="117.62976"
inkscape:cy="148.16686"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="8.3105459"
y="27.548409"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="8.3105459"
y="27.548409"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
<g
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
id="layer1-1-4"><path
id="cl-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-158.86395"
y="-214.4666"
id="text3788-8"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-7"
x="-158.86395"
y="-214.4666"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
<g
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
id="layer1-1-4-1"><path
id="cl-9-7"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-36.788386,-9.5311159)"
id="layer1-1-4-8"><path
id="cl-9-8"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,205.12954,253.27515)"
id="layer1-1-4-8-0"><path
id="cl-9-8-6"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-36.788386,60.169684)"
id="layer1-1-4-8-2"><path
id="cl-9-8-0"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g></svg>

After

Width:  |  Height:  |  Size: 9.0 KiB

@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="4_of_clubs.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/4_of_clubs.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3760"
cx="48.231091"
cy="18.137882"
fx="48.231091"
fy="18.137882"
r="9.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
id="linearGradient2984"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#000000;stop-opacity:0.65648854;"
offset="1"
id="stop2988" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784"
id="radialGradient3792"
cx="171.48665"
cy="511.22299"
fx="171.48665"
fy="511.22299"
r="81.902771"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3784"><stop
style="stop-color:#ffffff;stop-opacity:0.53435117;"
offset="0"
id="stop3786" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3855"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.51908398;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
r="81.902771"
fy="461.84113"
fx="181.69392"
cy="461.84113"
cx="181.69392"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3916"
xlink:href="#linearGradient3784-3"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-3"><stop
style="stop-color:#ffffff;stop-opacity:0.70229006;"
offset="0"
id="stop3786-86" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="2.4336873"
inkscape:cx="117.62976"
inkscape:cy="148.16686"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="8.3105459"
y="27.548409"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="8.3105459"
y="27.548409"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
<g
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
id="layer1-1-4"><path
id="cl-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-158.86395"
y="-214.4666"
id="text3788-8"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-7"
x="-158.86395"
y="-214.4666"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
<g
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
id="layer1-1-4-1"><path
id="cl-9-7"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-67.188386,-1.5311156)"
id="layer1-1-4-8"><path
id="cl-9-8"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,174.72954,245.27515)"
id="layer1-1-4-8-0"><path
id="cl-9-8-6"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-9.1115857,-1.5311131)"
id="layer1-1-4-8-2"><path
id="cl-9-8-66"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,232.80634,245.27515)"
id="layer1-1-4-8-0-4"><path
id="cl-9-8-6-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g></svg>

After

Width:  |  Height:  |  Size: 9.7 KiB

@@ -0,0 +1,238 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="5_of_clubs.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/5_of_clubs.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3760"
cx="48.231091"
cy="18.137882"
fx="48.231091"
fy="18.137882"
r="9.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
id="linearGradient2984"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#000000;stop-opacity:0.65648854;"
offset="1"
id="stop2988" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784"
id="radialGradient3792"
cx="171.48665"
cy="511.22299"
fx="171.48665"
fy="511.22299"
r="81.902771"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3784"><stop
style="stop-color:#ffffff;stop-opacity:0.53435117;"
offset="0"
id="stop3786" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3855"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.51908398;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
r="81.902771"
fy="461.84113"
fx="181.69392"
cy="461.84113"
cx="181.69392"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3916"
xlink:href="#linearGradient3784-3"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-3"><stop
style="stop-color:#ffffff;stop-opacity:0.70229006;"
offset="0"
id="stop3786-86" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="2.4336873"
inkscape:cx="117.62976"
inkscape:cy="148.16686"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="8.3105459"
y="27.548409"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="8.3105459"
y="27.548409"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
<g
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
id="layer1-1-4"><path
id="cl-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-158.86395"
y="-214.4666"
id="text3788-8"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-7"
x="-158.86395"
y="-214.4666"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
<g
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
id="layer1-1-4-1"><path
id="cl-9-7"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-67.188386,-1.5311156)"
id="layer1-1-4-8"><path
id="cl-9-8"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,174.72954,245.27515)"
id="layer1-1-4-8-0"><path
id="cl-9-8-6"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-9.1115857,-1.5311131)"
id="layer1-1-4-8-2"><path
id="cl-9-8-66"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,232.80634,245.27515)"
id="layer1-1-4-8-0-4"><path
id="cl-9-8-6-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-38.388386,61.769684)"
id="layer1-1-4-8-2-6"><path
id="cl-9-8-0"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g></svg>

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1,244 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="6_of_clubs.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/6_of_clubs.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3760"
cx="48.231091"
cy="18.137882"
fx="48.231091"
fy="18.137882"
r="9.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
id="linearGradient2984"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#000000;stop-opacity:0.65648854;"
offset="1"
id="stop2988" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784"
id="radialGradient3792"
cx="171.48665"
cy="511.22299"
fx="171.48665"
fy="511.22299"
r="81.902771"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3784"><stop
style="stop-color:#ffffff;stop-opacity:0.53435117;"
offset="0"
id="stop3786" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3855"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.51908398;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
r="81.902771"
fy="461.84113"
fx="181.69392"
cy="461.84113"
cx="181.69392"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3916"
xlink:href="#linearGradient3784-3"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-3"><stop
style="stop-color:#ffffff;stop-opacity:0.70229006;"
offset="0"
id="stop3786-86" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="2.4336873"
inkscape:cx="117.62976"
inkscape:cy="148.16686"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="8.3105459"
y="27.548409"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="8.3105459"
y="27.548409"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
<g
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
id="layer1-1-4"><path
id="cl-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-158.86395"
y="-214.4666"
id="text3788-8"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-7"
x="-158.86395"
y="-214.4666"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
<g
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
id="layer1-1-4-1"><path
id="cl-9-7"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,-9.5311159)"
id="layer1-1-4-8"><path
id="cl-9-8"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,177.92954,253.27515)"
id="layer1-1-4-8-0"><path
id="cl-9-8-6"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,60.169684)"
id="layer1-1-4-8-2"><path
id="cl-9-8-0"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,-9.7048439)"
id="layer1-1-4-8-8"><path
id="cl-9-8-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,230.7146,253.10142)"
id="layer1-1-4-8-0-2"><path
id="cl-9-8-6-6"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,59.995956)"
id="layer1-1-4-8-2-6"><path
id="cl-9-8-0-4"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g></svg>

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1,252 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="7_of_clubs.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/7_of_clubs.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3760"
cx="48.231091"
cy="18.137882"
fx="48.231091"
fy="18.137882"
r="9.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
id="linearGradient2984"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#000000;stop-opacity:0.65648854;"
offset="1"
id="stop2988" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784"
id="radialGradient3792"
cx="171.48665"
cy="511.22299"
fx="171.48665"
fy="511.22299"
r="81.902771"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3784"><stop
style="stop-color:#ffffff;stop-opacity:0.53435117;"
offset="0"
id="stop3786" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3855"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.51908398;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
r="81.902771"
fy="461.84113"
fx="181.69392"
cy="461.84113"
cx="181.69392"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3916"
xlink:href="#linearGradient3784-3"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-3"><stop
style="stop-color:#ffffff;stop-opacity:0.70229006;"
offset="0"
id="stop3786-86" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="2.4336873"
inkscape:cx="117.62976"
inkscape:cy="148.16686"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="8.3105459"
y="27.548409"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="8.3105459"
y="27.548409"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
<g
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
id="layer1-1-4"><path
id="cl-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-158.86395"
y="-214.4666"
id="text3788-8"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-7"
x="-158.86395"
y="-214.4666"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
<g
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
id="layer1-1-4-1"><path
id="cl-9-7"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,-27.131116)"
id="layer1-1-4-8"><path
id="cl-9-8"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,177.92954,269.27515)"
id="layer1-1-4-8-0"><path
id="cl-9-8-6"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,63.369684)"
id="layer1-1-4-8-2"><path
id="cl-9-8-0"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,-27.304844)"
id="layer1-1-4-8-8"><path
id="cl-9-8-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,230.7146,269.10142)"
id="layer1-1-4-8-0-2"><path
id="cl-9-8-6-6"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,63.195956)"
id="layer1-1-4-8-2-6"><path
id="cl-9-8-0-4"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-38.055702,18.622356)"
id="layer1-1-4-8-6"><path
id="cl-9-8-8"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g></svg>

After

Width:  |  Height:  |  Size: 12 KiB

@@ -0,0 +1,260 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="8_of_clubs.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/8_of_clubs.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3760"
cx="48.231091"
cy="18.137882"
fx="48.231091"
fy="18.137882"
r="9.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
id="linearGradient2984"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#000000;stop-opacity:0.65648854;"
offset="1"
id="stop2988" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784"
id="radialGradient3792"
cx="171.48665"
cy="511.22299"
fx="171.48665"
fy="511.22299"
r="81.902771"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3784"><stop
style="stop-color:#ffffff;stop-opacity:0.53435117;"
offset="0"
id="stop3786" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3855"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.51908398;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
r="81.902771"
fy="461.84113"
fx="181.69392"
cy="461.84113"
cx="181.69392"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3916"
xlink:href="#linearGradient3784-3"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-3"><stop
style="stop-color:#ffffff;stop-opacity:0.70229006;"
offset="0"
id="stop3786-86" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="2.4336873"
inkscape:cx="117.62976"
inkscape:cy="148.16686"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="8.3105459"
y="27.548409"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="8.3105459"
y="27.548409"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
<g
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
id="layer1-1-4"><path
id="cl-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-158.86395"
y="-214.4666"
id="text3788-8"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-7"
x="-158.86395"
y="-214.4666"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
<g
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
id="layer1-1-4-1"><path
id="cl-9-7"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,-27.131116)"
id="layer1-1-4-8"><path
id="cl-9-8"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,177.92954,269.27515)"
id="layer1-1-4-8-0"><path
id="cl-9-8-6"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,63.369684)"
id="layer1-1-4-8-2"><path
id="cl-9-8-0"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,-27.304844)"
id="layer1-1-4-8-8"><path
id="cl-9-8-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,230.7146,269.10142)"
id="layer1-1-4-8-0-2"><path
id="cl-9-8-6-6"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,63.195956)"
id="layer1-1-4-8-2-6"><path
id="cl-9-8-0-4"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(2.5125778,0,0,2.5125778,-38.055702,18.622356)"
id="layer1-1-4-8-6"><path
id="cl-9-8-8"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><g
transform="matrix(-2.5125778,0,0,-2.5125778,204.43127,226.5922)"
id="layer1-1-4-8-6-8"><path
id="cl-9-8-8-8"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g></svg>

After

Width:  |  Height:  |  Size: 12 KiB

@@ -0,0 +1,254 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="9_of_clubs.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/9_of_clubs.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3760"
cx="48.231091"
cy="18.137882"
fx="48.231091"
fy="18.137882"
r="9.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
id="linearGradient2984"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#000000;stop-opacity:0.65648854;"
offset="1"
id="stop2988" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784"
id="radialGradient3792"
cx="171.48665"
cy="511.22299"
fx="171.48665"
fy="511.22299"
r="81.902771"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3784"><stop
style="stop-color:#ffffff;stop-opacity:0.53435117;"
offset="0"
id="stop3786" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3855"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.51908398;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
r="81.902771"
fy="461.84113"
fx="181.69392"
cy="461.84113"
cx="181.69392"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3916"
xlink:href="#linearGradient3784-3"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-3"><stop
style="stop-color:#ffffff;stop-opacity:0.70229006;"
offset="0"
id="stop3786-86" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="2.4336873"
inkscape:cx="117.62976"
inkscape:cy="148.16686"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="8.3105459"
y="27.548409"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="8.3105459"
y="27.548409"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
<g
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
id="layer1-1-4"><path
id="cl-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-158.86395"
y="-214.4666"
id="text3788-8"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-7"
x="-158.86395"
y="-214.4666"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
<g
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
id="layer1-1-4-1"><path
id="cl-9-7"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 57.572834,25.099947 c 0,0 5.967372,-4.773898 5.967372,-11.392027 0,-3.8743954 -3.43972,-10.3065945 -11.392028,-10.3065945 -7.952308,0 -11.392028,6.4347116 -11.392028,10.3065945 0,6.618129 5.967373,11.392027 5.967373,11.392027 -6.62818,-5.163348 -18.444833,-1.638201 -18.444833,8.680956 0,5.16586 4.22113,10.849311 10.849311,10.849311 7.952308,0 11.392027,-8.680956 11.392027,-8.680956 0,0 1.010056,9.894531 -4.881939,15.191045 h 13.020178 c -5.891994,-5.294001 -4.881938,-15.191045 -4.881938,-15.191045 0,0 3.439718,8.680956 11.392027,8.680956 6.630693,0 10.849311,-5.685963 10.849311,-10.849311 0,-10.319157 -11.816654,-13.844304 -18.444833,-8.680956 z"
id="cl-9-8" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 57.110434,93.200747 c 0,0 5.967372,-4.773898 5.967372,-11.392027 0,-3.874396 -3.43972,-10.306594 -11.392028,-10.306594 -7.952308,0 -11.392028,6.434711 -11.392028,10.306594 0,6.618129 5.967373,11.392027 5.967373,11.392027 -6.62818,-5.163348 -18.444833,-1.638201 -18.444833,8.680953 0,5.16587 4.22113,10.84932 10.849311,10.84932 7.952308,0 11.392027,-8.68096 11.392027,-8.68096 0,0 1.010056,9.89453 -4.881939,15.19104 h 13.020178 c -5.891994,-5.294 -4.881938,-15.19104 -4.881938,-15.19104 0,0 3.439718,8.68096 11.392027,8.68096 6.630693,0 10.849311,-5.68597 10.849311,-10.84932 0,-10.319154 -11.816654,-13.844301 -18.444833,-8.680953 z"
id="cl-9-8-0" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 121.55789,24.926219 c 0,0 5.96737,-4.773898 5.96737,-11.392027 0,-3.8743954 -3.43971,-10.3065945 -11.39203,-10.3065945 -7.95231,0 -11.39202,6.4347116 -11.39202,10.3065945 0,6.618129 5.96737,11.392027 5.96737,11.392027 -6.62818,-5.163348 -18.444834,-1.638201 -18.444834,8.680956 0,5.16586 4.22113,10.849311 10.849304,10.849311 7.95231,0 11.39203,-8.680956 11.39203,-8.680956 0,0 1.01006,9.894531 -4.88193,15.191045 h 13.02017 c -5.89199,-5.294001 -4.88193,-15.191045 -4.88193,-15.191045 0,0 3.43971,8.680956 11.39202,8.680956 6.63069,0 10.84931,-5.685963 10.84931,-10.849311 0,-10.319157 -11.81665,-13.844304 -18.44483,-8.680956 z"
id="cl-9-8-9" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 121.55789,93.027019 c 0,0 5.96737,-4.773898 5.96737,-11.392028 0,-3.874395 -3.43971,-10.306593 -11.39203,-10.306593 -7.95231,0 -11.39202,6.434711 -11.39202,10.306593 0,6.61813 5.96737,11.392028 5.96737,11.392028 -6.62818,-5.163348 -18.444834,-1.638201 -18.444834,8.680951 0,5.16587 4.22113,10.84932 10.849304,10.84932 7.95231,0 11.39203,-8.68096 11.39203,-8.68096 0,0 1.01006,9.89453 -4.88193,15.19104 h 13.02017 c -5.89199,-5.294 -4.88193,-15.19104 -4.88193,-15.19104 0,0 3.43971,8.68096 11.39202,8.68096 6.63069,0 10.84931,-5.68597 10.84931,-10.84932 0,-10.319152 -11.81665,-13.844299 -18.44483,-8.680951 z"
id="cl-9-8-0-4" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 89.576544,59.281103 c 0,0 5.967372,-4.773897 5.967372,-11.392027 0,-3.874395 -3.43972,-10.306594 -11.392028,-10.306594 -7.952308,0 -11.392028,6.434712 -11.392028,10.306594 0,6.61813 5.967373,11.392027 5.967373,11.392027 C 72.099053,54.117756 60.2824,57.642902 60.2824,67.96206 c 0,5.165859 4.22113,10.84931 10.849311,10.84931 7.952308,0 11.392027,-8.680956 11.392027,-8.680956 0,0 1.010056,9.894531 -4.881939,15.191045 h 13.020178 c -5.891994,-5.294001 -4.881938,-15.191045 -4.881938,-15.191045 0,0 3.439718,8.680956 11.392027,8.680956 6.630694,0 10.849314,-5.685963 10.849314,-10.84931 0,-10.319158 -11.816657,-13.844304 -18.444836,-8.680957 z"
id="cl-9-8-8" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 110.06258,217.80216 c 0,0 -5.96737,4.77391 -5.96737,11.39203 0,3.8744 3.43971,10.3066 11.39202,10.3066 7.95232,0 11.39203,-6.43471 11.39203,-10.3066 0,-6.61812 -5.96737,-11.39203 -5.96737,-11.39203 6.62818,5.16335 18.44483,1.6382 18.44483,-8.68095 0,-5.16586 -4.22112,-10.84931 -10.84931,-10.84931 -7.95231,0 -11.39202,8.68095 -11.39202,8.68095 0,0 -1.01006,-9.89453 4.88193,-15.19104 h -13.02017 c 5.89199,5.294 4.88193,15.19104 4.88193,15.19104 0,0 -3.43972,-8.68095 -11.39203,-8.68095 -6.630687,0 -10.849305,5.68596 -10.849305,10.84931 0,10.31915 11.816655,13.8443 18.444835,8.68095 z"
id="cl-9-8-4" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 110.70832,149.70136 c 0,0 -5.96737,4.77391 -5.96737,11.39203 0,3.8744 3.43971,10.3066 11.39202,10.3066 7.95232,0 11.39203,-6.43471 11.39203,-10.3066 0,-6.61812 -5.96737,-11.39203 -5.96737,-11.39203 6.62818,5.16335 18.44483,1.6382 18.44483,-8.68095 0,-5.16586 -4.22112,-10.84931 -10.84931,-10.84931 -7.95231,0 -11.39202,8.68095 -11.39202,8.68095 0,0 -1.01006,-9.89453 4.88193,-15.19104 h -13.02017 c 5.89199,5.294 4.88193,15.19104 4.88193,15.19104 0,0 -3.43972,-8.68095 -11.39203,-8.68095 -6.630687,0 -10.849305,5.68596 -10.849305,10.84931 0,10.31915 11.816655,13.8443 18.444835,8.68095 z"
id="cl-9-8-0-2" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 46.077528,217.97589 c 0,0 -5.967372,4.77391 -5.967372,11.39203 0,3.8744 3.43972,10.3066 11.392028,10.3066 7.952308,0 11.392028,-6.43471 11.392028,-10.3066 0,-6.61812 -5.967373,-11.39203 -5.967373,-11.39203 6.62818,5.16335 18.444833,1.6382 18.444833,-8.68095 0,-5.16586 -4.22113,-10.84931 -10.849311,-10.84931 -7.952308,0 -11.392027,8.68095 -11.392027,8.68095 0,0 -1.010056,-9.89453 4.881939,-15.19104 H 44.992095 c 5.891994,5.294 4.881938,15.19104 4.881938,15.19104 0,0 -3.439718,-8.68095 -11.392027,-8.68095 -6.630693,0 -10.849311,5.68596 -10.849311,10.84931 0,10.31915 11.816654,13.8443 18.444833,8.68095 z"
id="cl-9-8-9-6" /><path
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 46.261118,149.87509 c 0,0 -5.967372,4.77391 -5.967372,11.39203 0,3.8744 3.43972,10.3066 11.392028,10.3066 7.952308,0 11.392028,-6.43471 11.392028,-10.3066 0,-6.61812 -5.967373,-11.39203 -5.967373,-11.39203 6.62818,5.16335 18.444833,1.6382 18.444833,-8.68095 0,-5.16586 -4.22113,-10.84931 -10.849311,-10.84931 -7.952308,0 -11.392027,8.68095 -11.392027,8.68095 0,0 -1.010056,-9.89453 4.881939,-15.19104 H 45.175685 c 5.891994,5.294 4.881938,15.19104 4.881938,15.19104 0,0 -3.439718,-8.68095 -11.392027,-8.68095 -6.630693,0 -10.849311,5.68596 -10.849311,10.84931 0,10.31915 11.816654,13.8443 18.444833,8.68095 z"
id="cl-9-8-0-4-9" /></svg>

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,258 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="A_of_clubs.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/A_of_clubs.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3760"
cx="48.231091"
cy="18.137882"
fx="48.231091"
fy="18.137882"
r="9.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
id="linearGradient2984"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#000000;stop-opacity:0.65648854;"
offset="1"
id="stop2988" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784"
id="radialGradient3792"
cx="171.48665"
cy="511.22299"
fx="171.48665"
fy="511.22299"
r="81.902771"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3784"><stop
style="stop-color:#ffffff;stop-opacity:0.53435117;"
offset="0"
id="stop3786" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788" /></linearGradient><filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter3834"
x="-0.13934441"
width="1.2786888"
y="-0.16242018"
height="1.3248404"><feGaussianBlur
inkscape:collect="always"
stdDeviation="9.5105772"
id="feGaussianBlur3836" /></filter><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3855"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.51908398;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter3834-6"
x="-0.13934441"
width="1.2786888"
y="-0.16242018"
height="1.3248404"><feGaussianBlur
inkscape:collect="always"
stdDeviation="9.5105772"
id="feGaussianBlur3836-6" /></filter><radialGradient
r="81.902771"
fy="461.84113"
fx="181.69392"
cy="461.84113"
cx="181.69392"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3916"
xlink:href="#linearGradient3784-3"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-3"><stop
style="stop-color:#ffffff;stop-opacity:0.70229006;"
offset="0"
id="stop3786-86" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-2" /></linearGradient><filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter3834-7"
x="-0.13934441"
width="1.2786888"
y="-0.16242018"
height="1.3248404"><feGaussianBlur
inkscape:collect="always"
stdDeviation="9.5105772"
id="feGaussianBlur3836-0" /></filter></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="2.4336873"
inkscape:cx="188.71531"
inkscape:cy="148.16686"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="6.7105455"
y="27.548409"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="6.7105455"
y="27.548409"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">A</tspan></text>
<g
transform="matrix(0.20614599,0,0,0.20614599,8.8705463,16.512759)"
id="g3804"><g
id="layer1-1"
transform="matrix(28.969925,0,0,28.969925,-1031.5368,-187.37665)"><path
style="fill:url(#radialGradient3760);fill-opacity:1"
inkscape:connector-curvature="0"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
id="cl" /></g><path
transform="matrix(1.1091261,0,0,1.2071687,-37.349149,-111.34227)"
sodipodi:nodetypes="cscsc"
inkscape:connector-curvature="0"
id="path3762"
d="m 117.3013,604.26609 c 0,0 -8.06755,-94.94997 22.85715,-122.85714 34.76052,-31.36871 140,-11.42857 140,-11.42857 0,0 -71.5404,24.83762 -100,48.57143 -27.21033,22.69199 -62.85715,85.71428 -62.85715,85.71428 z"
style="fill:url(#radialGradient3792);fill-opacity:1;stroke:none;filter:url(#filter3834)" /><path
transform="matrix(1.1091261,0,0,1.2071687,117.2523,-332.26545)"
sodipodi:nodetypes="cscsc"
inkscape:connector-curvature="0"
id="path3762-6"
d="m 117.3013,604.26609 c 0,0 -8.06755,-94.94997 22.85715,-122.85714 34.76052,-31.36871 140,-11.42857 140,-11.42857 0,0 -71.5404,24.83762 -100,48.57143 -27.21033,22.69199 -62.85715,85.71428 -62.85715,85.71428 z"
style="fill:url(#radialGradient3855);fill-opacity:1;stroke:none;filter:url(#filter3834-6)" /><path
transform="matrix(1.1420384,0.7029084,-0.84188482,1.367838,729.37187,-305.07466)"
sodipodi:nodetypes="cscsc"
inkscape:connector-curvature="0"
id="path3762-7"
d="m 117.3013,604.26609 c 0,0 -8.06755,-94.94997 22.85715,-122.85714 34.76052,-31.36871 140,-11.42857 140,-11.42857 0,0 -71.5404,24.83762 -100,48.57143 -27.21033,22.69199 -62.85715,85.71428 -62.85715,85.71428 z"
style="fill:url(#radialGradient3916);fill-opacity:1;stroke:none;filter:url(#filter3834-7)" /><path
id="rect3015"
d="m 28.355532,122.02522 0,734.28125 667.156248,0 0,-734.28125 -667.156248,0 z m 334.281258,97.625 c 91.68979,0 131.37499,74.17213 131.37499,118.84375 0,76.30678 -68.8125,131.34375 -68.8125,131.34375 76.42266,-59.5332 212.65625,-18.88573 212.65625,100.09375 0,59.5332 -48.64211,125.09375 -125.09375,125.09375 -91.68982,0 -131.34374,-100.09375 -131.34374,-100.09375 0,0 -11.65322,114.11662 56.28124,175.15625 l -150.12499,0 c 67.93447,-61.0686 56.3125,-175.15625 56.3125,-175.15625 0,0 -39.65394,100.09375 -131.34375,100.09375 -76.42266,0 -125.093758,-65.53158 -125.093758,-125.09375 0,-118.97948 136.233598,-159.62695 212.656258,-100.09375 0,0 -68.8125,-55.03697 -68.8125,-131.34375 0,-44.64265 39.65394,-118.84375 131.34375,-118.84375 z"
style="fill:#fffeff;fill-opacity:1;fill-rule:nonzero;stroke:none"
inkscape:connector-curvature="0" /></g><g
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
id="layer1-1-4"><path
id="cl-9"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-160.46396"
y="-214.4666"
id="text3788-8"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-7"
x="-160.46396"
y="-214.4666"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">A</tspan></text>
<g
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
id="layer1-1-4-1"><path
id="cl-9-7"
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
inkscape:connector-curvature="0"
style="fill:#000000" /></g></svg>

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 450 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.1 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.1 MiB

@@ -0,0 +1,401 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="10_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/10_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="0.4075976"
y="26.413288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="0.4075976"
y="26.413288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
<g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,210.91474)"
id="layer1-2-6-8-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,31.619539)"
id="layer1-2-6-8-2-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,151.18274)"
id="layer1-2-6-8-2-8-1"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,91.351539)"
id="layer1-2-6-8-2-8-1-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.89593,210.91474)"
id="layer1-2-6-8-8-9"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8-4"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.89593,31.619552)"
id="layer1-2-6-8-2-4-9"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3-0"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.89593,151.18274)"
id="layer1-2-6-8-2-8-1-9"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4-1"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.89593,91.351542)"
id="layer1-2-6-8-2-8-1-4-7"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4-9-7"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,83.213394,61.828949)"
id="layer1-2-6-8-2-4-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3-5"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,83.008034,180.83805)"
id="layer1-2-6-8-2-4-8-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3-5-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="13.216442"
y="26.376137"
id="text3788-43"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790-1"
x="13.216442"
y="26.376137"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">0</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-166.43544"
y="-215.98416"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-9"
x="-166.43544"
y="-215.98416"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-153.62659"
y="-216.0213"
id="text3788-43-2"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-1-0"
x="-153.62659"
y="-216.0213"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">0</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

@@ -0,0 +1,318 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="2_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/2_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter3834-6-0"
x="-0.13934441"
width="1.2786888"
y="-0.16242018"
height="1.3248404"><feGaussianBlur
inkscape:collect="always"
stdDeviation="9.5105772"
id="feGaussianBlur3836-6-6" /></filter><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="7.8456664"
y="26.413288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="7.8456664"
y="26.413288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-159.48785"
y="-216.71518"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-3"
x="-159.48785"
y="-216.71518"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
<g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,82.928726,184.02194)"
id="layer1-2-6-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,82.928726,55.619539)"
id="layer1-2-6-8-2"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g></svg>

After

Width:  |  Height:  |  Size: 12 KiB

@@ -0,0 +1,319 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="3_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/3_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="7.8456664"
y="26.413288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="7.8456664"
y="26.413288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-159.48785"
y="-216.71518"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-3"
x="-159.48785"
y="-216.71518"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
<g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,82.928726,192.02194)"
id="layer1-2-6-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,82.928726,50.819539)"
id="layer1-2-6-8-2"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,82.928726,120.52034)"
id="layer1-2-6-8-2-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g></svg>

After

Width:  |  Height:  |  Size: 12 KiB

@@ -0,0 +1,324 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="4_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/4_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="7.8456664"
y="26.413288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="7.8456664"
y="26.413288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-159.48785"
y="-216.71518"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-3"
x="-159.48785"
y="-216.71518"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
<g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.20553,184.02194)"
id="layer1-2-6-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.20553,55.619539)"
id="layer1-2-6-8-2"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,184.02194)"
id="layer1-2-6-8-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,55.619539)"
id="layer1-2-6-8-2-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g></svg>

After

Width:  |  Height:  |  Size: 12 KiB

@@ -0,0 +1,333 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="5_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/5_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="7.8456664"
y="26.413288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="7.8456664"
y="26.413288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-159.48785"
y="-216.71518"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-3"
x="-159.48785"
y="-216.71518"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
<g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.20553,184.02194)"
id="layer1-2-6-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.20553,55.619539)"
id="layer1-2-6-8-2"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,184.02194)"
id="layer1-2-6-8-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,55.619539)"
id="layer1-2-6-8-2-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,82.283636,119.47398)"
id="layer1-2-6-8-2-4-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g></svg>

After

Width:  |  Height:  |  Size: 13 KiB

@@ -0,0 +1,340 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="6_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/6_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="7.8456664"
y="26.413288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="7.8456664"
y="26.413288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-159.48785"
y="-216.71518"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-3"
x="-159.48785"
y="-216.71518"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
<g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,110.12873,192.02194)"
id="layer1-2-6-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,110.12873,50.819539)"
id="layer1-2-6-8-2"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,110.12873,120.52034)"
id="layer1-2-6-8-2-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,56.013391,192.14005)"
id="layer1-2-6-8-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,56.013391,50.937663)"
id="layer1-2-6-8-2-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,56.013391,120.63845)"
id="layer1-2-6-8-2-8-1"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g></svg>

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,349 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="7_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/7_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="7.8456664"
y="26.413288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="7.8456664"
y="26.413288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-159.48785"
y="-216.71518"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-3"
x="-159.48785"
y="-216.71518"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
<g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,111.72873,193.62194)"
id="layer1-2-6-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,111.72873,49.219539)"
id="layer1-2-6-8-2"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,111.72873,120.52034)"
id="layer1-2-6-8-2-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.413391,193.74005)"
id="layer1-2-6-8-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.413391,49.337663)"
id="layer1-2-6-8-2-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.413391,120.63845)"
id="layer1-2-6-8-2-8-1"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,83.213393,85.53779)"
id="layer1-2-6-8-2-4-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g></svg>

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,358 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="8_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/8_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="7.8456664"
y="26.413288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="7.8456664"
y="26.413288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-159.48785"
y="-216.71518"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-3"
x="-159.48785"
y="-216.71518"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
<g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,111.72873,193.62194)"
id="layer1-2-6-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,111.72873,49.219539)"
id="layer1-2-6-8-2"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,111.72873,120.52034)"
id="layer1-2-6-8-2-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.413391,193.74005)"
id="layer1-2-6-8-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.413391,49.337663)"
id="layer1-2-6-8-2-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.413391,120.63845)"
id="layer1-2-6-8-2-8-1"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,83.213393,85.53779)"
id="layer1-2-6-8-2-4-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,83.21339,156.92384)"
id="layer1-2-6-8-8-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g></svg>

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,367 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="9_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/9_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="7.8456664"
y="26.413288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="7.8456664"
y="26.413288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-159.48785"
y="-216.71518"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-3"
x="-159.48785"
y="-216.71518"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
<g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,210.91474)"
id="layer1-2-6-8-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,31.619539)"
id="layer1-2-6-8-2-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,151.18274)"
id="layer1-2-6-8-2-8-1"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,91.351539)"
id="layer1-2-6-8-2-8-1-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.89593,210.91474)"
id="layer1-2-6-8-8-9"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8-4"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.89593,31.619552)"
id="layer1-2-6-8-2-4-9"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3-0"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.89593,151.18274)"
id="layer1-2-6-8-2-8-1-9"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4-1"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.89593,91.351542)"
id="layer1-2-6-8-2-8-1-4-7"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-8-4-9-7"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,83.213394,61.828949)"
id="layer1-2-6-8-2-4-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3-5"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g></svg>

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,311 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="ace_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/ace_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter3834-6-0"
x="-0.13934441"
width="1.2786888"
y="-0.16242018"
height="1.3248404"><feGaussianBlur
inkscape:collect="always"
stdDeviation="9.5105772"
id="feGaussianBlur3836-6-6" /></filter><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="6.2456665"
y="28.013288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="6.2456665"
y="28.013288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial;fill:#df0000;fill-opacity:1">A</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-161.08786"
y="-213.51517"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-3"
x="-161.08786"
y="-213.51517"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">A</tspan></text>
<g
transform="matrix(0.17001436,0,0,0.17001436,19.517107,29.794341)"
id="g3011"><g
id="layer1-2"
transform="matrix(35.005102,0,0,35.005102,369.18369,512.27289)"><path
sodipodi:nodetypes="ccccccccc"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
id="dl"
inkscape:connector-curvature="0"
style="fill:url(#radialGradient3137);fill-opacity:1" /></g><path
transform="matrix(-1.4652123,0.23694327,-0.24538129,-1.5173914,660.30624,1148.701)"
sodipodi:nodetypes="cscsc"
inkscape:connector-curvature="0"
id="path3762-6"
d="m 117.3013,604.26609 c 0,0 -8.06755,-94.94997 22.85715,-122.85714 34.76052,-31.36871 140,-11.42857 140,-11.42857 0,0 -71.5404,24.83762 -100,48.57143 -27.21033,22.69199 -62.85715,85.71428 -62.85715,85.71428 z"
style="fill:url(#radialGradient3100);fill-opacity:1;stroke:none;filter:url(#filter3834-6-0)" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g></svg>

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 395 KiB

Some files were not shown because too many files have changed in this diff Show More