# Rozpisky – generátor výkresových rohových razítek (C# / .NET) Standalone WPF aplikace (náhrada původního excelového nástroje „Rozpisky 1.0.8"). Spravuje projekt, seznam příloh, revize a číselníky, a z **DXF šablony rozpisky** generuje **náhled**, **PDF na A4**, **hromadný DXF** (pole jako `TEXT`/`MTEXT` entity), **seznam příloh do XLSX** a přes připojený CAD i **tisk rozvržení**. Celé kreslení stojí na jedné myšlence: existuje jedno **kreslicí rozhraní** ([`IProfileRenderer`](Rozpisky/Rendering/IProfileRenderer.cs) – „kam se kreslí") a několik jeho implementací. Tatáž kresba teče na obrazovku (náhled), do PNG, do PDF i do DXF. Čtečka šablony ([`DxfTemplate`](Rozpisky/Dxf/DxfTemplate.cs)) jen čte DXF a „přehrává" ho do tohoto rozhraní. --- ## 1. NuGet balíčky Cílový framework: **`net9.0-windows`**, `UseWPF=true`, platforma **AnyCPU**. Připojení na běžící CAD a MS Excel jde přes COM (pozdní vazba přes `dynamic`) – bitness běhového procesu se řídí hostitelem, zvláštní `PlatformTarget` se nenastavuje. | Balíček | Verze | K čemu | Klíčové API | |---|---|---|---| | **SkiaSharp** | `2.88.8` | 2D rasterizace + **PDF**. Kreslení geometrie/textu/obrázků; export PNG i PDF. | `SKCanvas`, `SKPaint`, `SKPath`, `SKBitmap`, `SKImage`, `SKTypeface`, `SKDocument.CreatePdf`, `SKFileWStream` | | **SkiaSharp.Views.WPF** | `2.88.8` | WPF plátno pro živý náhled (zoom/posun). **Jen pro GUI** – headless export ho nepotřebuje. | `SKElement`, `OnPaintSurface(SKPaintSurfaceEventArgs)` | | **ACadSharp** | `3.6.29` | Čtení i **zápis** DXF (novější verze, výplně HATCH, XRecord metadata atributů). Nahradilo dřívější netDxf. | `DxfReader.Read`, `DxfWriter.Write`, `CadDocument`, `ACadSharp.Entities.*`, `ACadSharp.Tables.TextStyle` | | **ClosedXML** | `0.105.0` | Vyplnění mustru „Seznam příloh" (`SEZNAM.xlsx`) při exportu seznamu. | `XLWorkbook`, `IXLWorksheet`, `Cell(...).Value`, `SaveAs` | > **Pozn. k ACadSharp:** na rozdíl od netDxf vystaví font ze stylu i box-šířku víceřádkových atributů > (XRecord, group 41), takže jde dlouhý text v poli správně zalomit. DXF se zapisuje **binárně** > (`DxfWriter.Write(..., binary: true)`) – výrazně menší soubor. Převod XLSX → PDF (tisk seznamu) jde > přes **nainstalovaný MS Excel** (COM), ne přes ACadSharp/ClosedXML. ### Zápis DXF – co je potřeba vědět - **Tloušťka čáry** = `Layer.LineWeight` (enum, hodnota v 1/100 mm – standardní hodnoty: `0,5,9,13,15,18,20,25,30,35,40,50,53,60,70,80,90,100,106,120,140,158,200,211`). - **Diakritika** se do DXF ukládá jako Unicode escape `\U+XXXX` (např. `š` → `\U+0161`) – AutoCAD i ostatní čtečky to čtou správně. - Pole, která byla v šabloně `ATTDEF`, se při vyplnění zapisují jako obyčejné `TEXT`/`MTEXT` entity (žádné atributy bloku). - Objekty v netisknutelných hladinách (`PlotFlag=0`) se **nevykreslují**, ale ví se o nich kvůli místům pro loga (viz [`DxfTemplate.PlaceholderHladiny`](Rozpisky/Dxf/DxfTemplate.cs)). --- ## 2. Struktura kódu Aplikace je členěná dle vrstev (MVVM + služby): ``` Rozpisky/ ├─ Rendering/ ← kreslicí vrstva (nezávislá na cíli) │ ├─ IProfileRenderer.cs ← kreslicí rozhraní + HAlign/VAlign + TextStyle │ ├─ SkiaProfileRenderer.cs ← výstup přes SkiaSharp (obrazovka / PNG / PDF) │ ├─ DxfProfileRenderer.cs ← výstup do DXF (ACadSharp) │ ├─ BoundsRenderer.cs ← spočítá bbox kresby (fit / umístění na A4) │ └─ TransformRenderer.cs ← dekorátor: měřítko + posun (fit fragmentů/loga) ├─ Dxf/ ← čtení šablony, mapování hodnot, kódy │ ├─ DxfTemplate.cs ← načte DXF šablonu a přehraje ji do IProfileRenderer │ ├─ RozpiskaRenderer.cs ← spojuje šablonu + hodnoty + exportéry (fasáda pro UI) │ ├─ RozpiskaValues.cs ← „tag DXF → hodnota" z projektu/přílohy/číselníků (reflexí dle mapovani.json) │ ├─ MapovaniStore.cs ← načtení mapování z mapovani.json (config vedle .exe) │ ├─ KodPrilohy.cs ← 47-poziční strojový kód přílohy (manuál SŽ kap. 3) │ ├─ Exporters.cs ← PngA4 / PdfA4 / RenderView (kreslí libovolný Action) │ └─ LogaResolver.cs ← dohledání DXF log/schémat k příloze ├─ Cad/ ← připojení na běžící CAD (COM) + aktualizace/tisk rozvržení ├─ Xlsx/ ← export seznamu příloh (ClosedXML) + XLSX→PDF (Excel COM) ├─ Data/ ← persistence: .rzp projekt, číselníky, atomický zápis configů ├─ Models/ ← datový model (Projekt, Priloha, Revize, Ciselniky, Nastaveni…) ├─ ViewModels/ ← MainViewModel (stav, akce, dirty-flag, export) ├─ Views/ + Themes/ + Theming/ + Behaviors/ ← WPF UI (dark theme, grid chování) ├─ App.xaml(.cs) ← vstupní bod + globální záchytná síť výjimek (error.log) └─ MainWindow.xaml(.cs) ← hlavní okno ``` Vedle .exe se dodávají editovatelné soubory: `Rozpiska.dxf` (šablona), `SEZNAM.xlsx` (mustr seznamu), `ciselniky.json`, `mapovani.json`, `cad.json`, `layout.json`, `loga/` a manuál SŽ (PDF). --- ## 3. Kreslicí rozhraní Vše se kreslí ve **výkresových milimetrech**, osa **Y nahoru** (jako v CAD/DXF). ```csharp public enum HAlign { Left, Center, Right } public enum VAlign { Baseline, Bottom, Middle, Top } public readonly record struct TextStyle(string? FontFamily = null, bool Bold = false, bool Italic = false, string? FontFile = null); public interface IProfileRenderer { void Layer(string name, int rgb, double lineWeightMm = 0.25); // barva 0xRRGGBB, tloušťka ByLayer void Line(double x1, double y1, double x2, double y2); void Polyline(IReadOnlyList<(double X, double Y)> pts, bool closed = false); void Circle(double cx, double cy, double r); void Text(double x, double y, string text, double heightMm, HAlign h = HAlign.Left, VAlign v = VAlign.Baseline, double rotationDeg = 0, TextStyle style = default, double widthFactor = 1, double maxWidthMm = 0); void Fill(IReadOnlyList> loops); // HATCH výplně (even-odd) } ``` - `rgb` je barva `0xRRGGBB` rozbalená z DXF (ByLayer i index/true-color). Bílá se kreslí černě (bílá na bílém papíře by zmizela). - `widthFactor` = horizontální měřítko písma (DXF width factor). `maxWidthMm` = šířka pro zalomení víceřádkového textu (0 = jen explicitní zlomy). Implementace: - **`SkiaProfileRenderer`** – kreslí na `SKCanvas`. Dostane funkci `Map(mmX, mmY) → bod plátna` a `pxPerMm`. Text vzpřímeně (rotace opačně, plátno má Y dolů), fonty ze stylu/souboru s fallbackem. Je `IDisposable` (uvolní typefaces) – používat přes `using`. - **`DxfProfileRenderer`** – pro každou hladinu založí `Layer` (barva + tloušťka), entity přidává do `Document`. Výsledek zapíše `DxfWriter.Write(path, renderer.Document, binary: true)`. - **`BoundsRenderer`** – nic nekreslí, jen sčítá min/max souřadnic (fit / vystředění na A4). - **`TransformRenderer`** – dekorátor: na souřadnice aplikuje uniformní měřítko + posun. Slouží k umístění vkládaného DXF fragmentu (logo / schéma) do vymezeného rámečku. --- ## 4. Čtečka šablony a fasáda pro UI Nízkoúrovňová čtečka [`DxfTemplate`](Rozpisky/Dxf/DxfTemplate.cs): ```csharp var tpl = new DxfTemplate("Rozpiska.dxf"); tpl.Replay(g); // NÁHLED: pole se vykreslí jako jejich názvy (tagy) tpl.Replay(g, values); // VYPLNĚNÍ: pole = hodnota; nevyplněné = prázdné tpl.Replay(g, values, includeNoPrint); // + volitelně A4 rámeček !!NOPRINT (pro DXF export/tisk) ``` `Replay` přehraje statickou geometrii (`Line`/`Polyline`), popisky (`Text`/`MText`) a pole (`ATTDEF` z model space) do rozhraní. `SheetFrame` vrací A4 rámeček šablony (referenční obdélník pro vystředění), `Placeholders` vrací rámečky pro loga/schéma podle vyhrazených hladin, `Bounds` bbox kresby (pro fit vkládaného fragmentu). Fasáda pro UI [`RozpiskaRenderer`](Rozpisky/Dxf/RozpiskaRenderer.cs) spojuje šablonu, mapování hodnot a exportéry na jedno místo (šablona se cachuje): ```csharp var r = new RozpiskaRenderer(Nastaveni.SablonaDxf); Action draw = r.BuildDraw(projekt, priloha, revize, ciselniky, vyplnene: true, loga, ramecekA4: false); r.ExportPdf(projekt, priloha, revize, ciselniky, "rozpiska.pdf", loga); // A4 PDF vč. log r.ExportPng(projekt, priloha, revize, ciselniky, "nahled.png", loga); // A4 PNG ``` Hodnoty polí sestavuje [`RozpiskaValues`](Rozpisky/Dxf/RozpiskaValues.cs): mapování „pole programu → DXF atribut" **není v kódu**, čte se z `mapovani.json` a hodnoty se dotahují reflexí. K tomu se algoritmicky doplní 47-poziční strojový kód (`K1`–`K47`, viz [`KodPrilohy`](Rozpisky/Dxf/KodPrilohy.cs)). --- ## 5. Export na A4 (`Exporters`) [`Exporters`](Rozpisky/Dxf/Exporters.cs) vykreslí **libovolný** `Action`: ```csharp Exporters.PngA4(draw, "rozpiska.png", dpi: 200, frame: sheetFrame); // A4 raster Exporters.PdfA4(draw, "rozpiska.pdf", frame: sheetFrame); // A4 vektor (PDF, 1:1) Exporters.RenderView(canvas, wpx, hpx, draw, frame, zoom, panX, panY); // živý náhled na SKElement ``` Princip A4: referenční obdélník (`frame` = A4 rámeček šablony, jinak `BoundsRenderer`) se mapuje **1:1** a vystředí na papír; zmenší se jen tehdy, když se do tisknutelné plochy nevejde. PDF se kreslí přes `SKDocument.CreatePdf` (1 mm = 72/25,4 bodu; text vektorový, tloušťky v reálných mm). --- ## 6. Demo data (`DemoData`) [`DemoData`](Rozpisky/Data/DemoData.cs) naplní úvodní stav při startu: `SeedProjekt(vm)` (ukázkový projekt + přílohy), `SeedCiselniky(c)` a `SeedKraje(c)` (výchozí číselníky, pokud `ciselniky.json` chybí). --- ## 7. Toky exportu (přehled) | Akce | Vstup | Výstup | Přes | |---|---|---|---| | Náhled rozpisky | příloha | okno se SKElement | `RozpiskaRenderer.BuildDraw` → `Exporters.RenderView` | | Export do PDF (výběr/vše) | přílohy | `Rozpisky\*.pdf` | `RozpiskaRenderer.ExportPdf` (per-item, chyby se sbírají) | | Export do DXF (hromadně) | přílohy | `__ Otevřená\_rozpisky.dxf` | dlaždicová mřížka do `DxfProfileRenderer` → `DxfWriter.Write` | | Export seznamu | objekt | `SEZNAM_.xlsx` vedle .rzp | `SeznamExporter` (ClosedXML mustr) | | Tisk seznamu | dřív exportovaný XLSX | `Přílohy\*.pdf` | `ExcelToPdf` (MS Excel COM) | | Aktualizace/tisk rozvržení | přílohy | přejmenování/tisk v CADu | `Cad/CadLayoutService`, `Cad/CadTiskService` (COM) | Aplikace **nemá CLI režim** – vše jede z GUI. Neočekávané výjimky zachytává globální síť v [`App.xaml.cs`](Rozpisky/App.xaml.cs) a zapisuje je do `error.log` vedle .exe. --- ## 8. Persistence a robustnost - **Projekt `.rzp`** ([`ProjektStore`](Rozpisky/Data/ProjektStore.cs)) – JSON s exkluzivním zámkem drženým po dobu otevření (ostatní procesy smí jen číst). Zápis serializuje **před** useknutím souboru, aby pád nezanechal prázdný soubor. - **Configy vedle .exe** (`ciselniky.json`, `cad.json`, `layout.json`, `mapovani.json`) jdou přes [`JsonConfigFile`](Rozpisky/Data/JsonConfigFile.cs): **atomický zápis** (přes `.tmp`) a při poškozeném souboru **záloha do `.bak` + upozornění** (místo tichého přepsání výchozími daty). - **Zamčený cíl exportu** – před zápisem PDF/XLSX/DXF se ověří, zda soubor nedrží jiný program (Excel, prohlížeč PDF), a nabídne Pokračovat/Přerušit. --- ## 9. Závislost na šabloně Šablona je libovolný DXF, kde: - rám a mřížku tvoří `LINE` / `LWPOLYLINE`, - pevné popisky jsou `TEXT` / `MTEXT`, - **vyplňovaná pole jsou `ATTDEF`** v model space (jejich `Tag` = klíč do hodnot), - placeholdery pro loga jsou uzavřené polylinie na vyhrazených netisknutelných hladinách (`Rozpiska_logo1`…, `Rozpiska_Orientační schéma`) – logo/schéma se fitne do jejich ohraničení, samotný placeholder se nekreslí (`PlotFlag=0`), - A4 hranici papíru vymezuje rámeček na hladině `!!NOPRINT`. Tím je celý vzhled rozpisky daný **datovým souborem a mapováním** (`Rozpiska.dxf` + `mapovani.json`), ne kódem – pro jinou rozpisku stačí jiný DXF a mapování.