Files
Rozpisky/docs/ROZPISKY.md
T
marekandClaude Opus 5 d4eb3f2102 Restrukturalizace složky podle účelu podkladů
Kořen mísil tři nesouvisející věci. Nově:
- docs/            – podklady jen pro tvorbu programu (ROZPISKY, ROZHRANI,
                     PODKLAD_CAD, manuál SŽ v .md)
- Rozpisky/Podklady/ – podklady nutné k běhu, dodávané vedle .exe
                     (Rozpiska.dxf, SEZNAM.xlsx, manuál .pdf, ciselniky.json,
                     mapovani.json, loga/)
- vzorky/          – cizí vstupy pro ruční zkoušení (schema.dxf)

Mění se jen zdrojový strom. Rozvržení vedle .exe zůstává ploché (+ loga\)
přes metadata Link v .csproj, takže všechny resolvery nad AppContext.BaseDirectory
fungují beze změny. Konec odkazů přes ..\ ven z projektu.

Doprovodně: cesty v TestData.Logo, odkazy v dokumentaci, CLAUDE.md a v promptech
subagentů.

Build 0 chyb / 0 varování, 36 testů zelených.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 10:35:50 +02:00

13 KiB
Raw Blame History

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 „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) jen čte DXF a „přehrává" ho do tohoto rozhraní.


1. NuGet balíčky

Cílový framework: net10.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).

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<IProfileRenderer>)
│  └─ 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).

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<IReadOnlyList<(double X, double Y)>> 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:

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 spojuje šablonu, mapování hodnot a exportéry na jedno místo (šablona se cachuje):

var r = new RozpiskaRenderer(Nastaveni.SablonaDxf);
Action<IProfileRenderer> 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: 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 (K1K47, viz KodPrilohy).


5. Export na A4 (Exporters)

Exporters vykreslí libovolný Action<IProfileRenderer>:

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 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.BuildDrawExporters.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 DxfProfileRendererDxfWriter.Write
Export seznamu objekt SEZNAM_<objekt>.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 a zapisuje je do error.log vedle .exe.


8. Persistence a robustnost

  • Projekt .rzp (ProjektStore) 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: 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í.