Filee chooses engines automatically. Settings → Engines shows their status, lets you change the priority (higher wins when two engines can do the same conversion), and install or remove the optional engines.
| Engine | How it gets there | Handles |
|---|---|---|
| ImageMagick (Magick.NET) | built in (library) | JPG, PNG, WEBP, TIFF, BMP, GIF, ICO, AVIF, JPEG XL, JPEG 2000, PSD/PSB, TGA, PPM, MNG, QOI, DDS, PCX, CUR, XPM, XBM, WBMP, PAM, PFM, SGI, Sun Raster, MIFF ↔ each other, and APNG (Filee's own reader and writer); HEIC (written with FFmpeg), XCF, camera RAW (LibRaw), DICOM (uncompressed ones by Filee's own reader), Photo CD, OpenEXR, Radiance HDR and EMF/WMF (Windows) read |
| Vector graphics | built in (Svg.Skia, SkiaSharp, ACadSharp) | SVG/SVGZ → PDF (vector) and images, SVG ↔ SVGZ, SVG → DXF (→ DWG with the CAD engine), raster images → SVG (tracing), PDF-compatible AI → PDF |
| ICNS | built in | images → ICNS (16–1024 px), ICNS → images |
| PDFsharp | built in (library) | images → PDF, merge, split, page ranges, compression (smaller PDFs, see below) |
| PDFium | built in (library) | PDF → images (multi-page TIFF/GIF supported) |
| Unhwp | built in (library) | HWP/HWPX → TXT, Markdown, HTML |
| Markdown (Markdig) | built in (library) | Markdown → HTML, TXT (and → PDF / DOCX through the HWPX writer, rhwp or LibreOffice) |
| Spreadsheets (ExcelDataReader for XLS) | built in (library) | XLSX, XLS, ODS, CSV, TSV → XLSX, ODS, CSV, TSV (one CSV / TSV per sheet) |
| Office Open XML | built in | DOCM / DOTX / DOTM ↔ DOCX, XLSM / XLTX ↔ XLSX, PPTM / POTX / PPSX ↔ PPTX (macros removed for macro-free types) |
| Fonts | built in | TTF, OTF, WOFF, WOFF2, EOT ↔ each other; CFF (PostScript) outlines become TrueType for TTF and EOT |
| HWPX writer | built in | DOCX (+ DOCM/DOTX/DOTM), XLSX (+ XLSM/XLTX), XLS, ODS, CSV, TSV, PPTX (+ PPTM/POTX/PPSX), PDF, HTML, EPUB, MOBI/AZW3, FB2, HWPX, TXT, Markdown → HWPX (and with rhwp → PDF and images); ODT, RTF, reStructuredText, LaTeX → HWPX with Pandoc |
| DOCX writer | built in | HWPX (and HWP through rhwp), PDF, HTML, EPUB, MOBI/AZW3, FB2, Markdown, TXT, XLSX, XLS, ODS, CSV, PPTX (+ variants) → DOCX |
| PDF text | built in (PdfPig) | PDF → TXT (reading order, also two columns; no OCR) |
| PDF slides | built in (PDFium, PdfPig) | PDF → PPTX: a picture of each page on its own slide (looks like the PDF, not editable), the page's text in the speaker notes |
| built in (MimeKit, MsgReader) | EML, Outlook MSG → HTML (headers + body, inline pictures), TXT, ZIP (the attachments); MSG → EML | |
| Data | built in (System.Text.Json, YamlDotNet, Tomlyn, System.Xml) | JSON, JSON Lines, YAML, TOML, XML, CSV ↔ each other (CSV for tables); vCard ↔ CSV; spreadsheets and contacts reach JSON / YAML / TOML / XML through CSV |
| E-books | built in | EPUB, MOBI/AZW/AZW3/PRC, FB2, HTMLZ, TXTZ → EPUB, HWPX (→ PDF), TXT, HTML, Markdown, FB2, HTMLZ, TXTZ; HTML, Markdown, TXT, DOCX → EPUB/FB2/HTMLZ/TXTZ; HTML → TXT; comics CBZ/CBR/CB7/CBT/CBC → PDF, EPUB, CBZ; PDF → CBZ; AZW4 → PDF; EPUB ↔ KEPUB (Kobo) |
| CAD (ACadSharp + built-in renderer) | built in (library) | DWG ↔ DXF; DWG/DXF → PDF (vector), SVG, PNG, JPG, WEBP, TIFF, BMP, GIF, ICO, AVIF |
| Subtitles | built in | SRT, ASS/SSA, WebVTT, SAMI (SMI), LRC, TTML, MicroDVD, SCC → SRT, ASS, WebVTT, SAMI, LRC, TTML, MicroDVD, always UTF-8 (Korean CP949 / EUC-KR files read correctly) |
| 3D models (SharpGLTF for glTF) | built in (library) | GLB, glTF, STL, OBJ (+ MTL), 3MF, PLY ↔ each other; → PNG, JPG, … (an isometric preview) |
| rhwp | bundled with the installer (engines/rhwp) |
HWP/HWPX → PDF, HWP → HWPX, HWPX → HWP |
| Archives (7-Zip) | bundled with the installer (engines/7zip, ~2.5 MB) + built-in readers |
ZIP, 7Z, RAR, TAR (+ GZ/BZ2/XZ/Z/7Z/LZ), CAB, ISO, DMG, … → folder, ZIP, 7Z, TAR, TAR.GZ/BZ2/XZ; ALZ, EGG, lzip read in-process; "Compress into one archive" for any files |
| LibreOffice + H2Orestart + Java | optional download (~420 MB, 1.3 GB on disk) | older and rare formats only: DOC, XLS, PPT, RTF and OpenDocument → PDF and to each other, output as DOCX/ODT/ODS/ODP; HWP/HWPX → DOCX/ODT; read-only formats (see below) → PDF and their kind's editable formats; FODT, OpenDocument templates (OTT, OTS, OTP) and XLT out; SVG → EMF/WMF/ODG, a PDF page → SVG |
| Pandoc | optional download (~42 MB, 240 MB on disk) | Markdown ↔ DOCX/ODT/RTF, DOCX/ODT/HTML/RTF → Markdown, HTML ↔ DOCX/ODT, markup languages (reStructuredText, LaTeX, Org, AsciiDoc, Typst, MediaWiki, DocBook, Jupyter, Textile, man, OPML) ↔ Markdown/HTML/DOCX/ODT and each other (and → RTF, EPUB, TXT), Markdown and markup → PPTX slides |
| Ghostscript | optional download (~20 MB, 31 MB on disk) | EPS/PS → PDF, PDF → EPS/PS, PostScript-based AI → PDF (images through PDF and PDFium); smaller PDFs when a preset asks for it |
| FFmpeg | optional download (~100 MB, 272 MB on disk) | all video and audio: video ↔ video (H.264, HEVC, AV1), video → animated GIF / APNG, a still frame or every frame, audio and subtitle extraction, audio ↔ audio (also APE, WavPack, TTA, DSD, DTS, ALAC, …), GIF, APNG and photos → MP4/WEBM/MOV, images → HEIC |
| Calibre | optional download (~216 MB, 660 MB on disk) | rare e-book formats: LIT, LRF, CHM, PDB, PML, RB, SNB, TCR, OEB → EPUB; EPUB → MOBI, AZW3, LIT, LRF, PDB, PML, RB, SNB, TCR; e-books → PDF (ranked above the HWPX writer, see below) |
DOCX, XLSX, XLS, ODS and PPTX → PDF need neither Microsoft Office nor LibreOffice: they are read in-process, written as HWPX and rendered by rhwp. Anything the built-in readers understand (including PDF) is written as DOCX by the DOCX writer. LibreOffice's and Pandoc's costs are set so the built-in route always wins where one exists.
All built-in document readers are registered in one place, Hwp/Hwpx/DocumentReaders.cs (format id → reader, and
which formats need Pandoc). The HWPX writer and the DOCX writer take their input formats from it: adding a reader
there is one line and makes both writers (and every route through them) accept the format.
Every engine is built in, bundled with the installer or downloaded into Filee's own folder, so a conversion behaves the same on every PC. An external engine is looked for in this order:
- a copy the user picked in Settings → Engines;
- Filee's own copy (bundled or downloaded);
- a copy already on the computer: on
PATH, in Homebrew's and the usual Unix folders, or where the LibreOffice, calibre and Ghostscript installers put it. Settings → Engines shows it as "Found on this PC", with a note when its version is older than the one Filee downloads.
Where Filee has no download of an engine (FFmpeg and Ghostscript on macOS, LibreOffice and Ghostscript on Linux),
step 3 is how it is found. Microsoft Office
and Hancom Office are never used, even when installed. Running from source, the repository's engines/ folder
(filled by build/fetch-engines.ps1) is used.
The installer contains only the small engines. On first start Filee asks which of the large ones to download; they can be installed or removed later in Settings → Engines. A conversion that needs a missing engine shows "Needs LibreOffice …" on its donut slice.
- Downloads are pinned in
src/Filee.Engines/Infrastructure/engines.json(URL, SHA-256, size). The same file is read bybuild/fetch-engines.ps1, so development and the app always use the same versions. EngineInstallerverifies the SHA-256 before unpacking and unpacks into a staging folder that replaces the engine folder only when complete. Redirects to mirrors (even plain HTTP) are followed, because the hash decides.- Engines go to
%LOCALAPPDATA%\Filee\engines: outside the app folder, so updates keep them, and inside Filee's install root, so uninstalling removes them. - LibreOffice and calibre come as MSIs and are unpacked with an administrative install (
msiexec /a): files only, no registry entries, no admin rights. LibreOffice's help, gallery and most dictionaries are removed afterwards (~500 MB). - Ghostscript comes from conda-forge (Artifex publishes only an NSIS installer): a
.condapackage is a zip with a zstd tarball, of which onlyLibrary/binis unpacked (SharpCompress;fetch-engines.ps1uses Windows'tar.exe). Its fonts and resources are compiled intogsdll64.dll. The Microsoft C++ runtime it was built against (vcruntimecomponent) is copied next togswin64c.exe, so it runs on PCs without the VC++ Redistributable.
To bump a version, change url, sha256 and size in engines.json together, run
pwsh build/fetch-engines.ps1, test, and update THIRD-PARTY-NOTICES.md. For rhwp, also update RHWP_VERSION,
RHWP_COMMIT and RUST_TOOLCHAIN in build/build-linux-native.py (see below); CI fails until they match. When the
LibreOffice team retires a version from download.documentfoundation.org/libreoffice/stable/, its URL stops working:
keep the pinned version current.
Linux packages run on Ubuntu 22.04 (glibc 2.35). rhwp's Linux release is linked on Ubuntu 24.04 (needs glibc 2.39).
The linux-native job of .github/workflows/portable.yml builds it from its pinned, unmodified source on Ubuntu 22.04
(x64 and arm64) with build/build-linux-native.py:
rhwp:cargo build --release --locked --bin rhwpat the tag's commit with its pinned Rust toolchain, in the layout of rhwp's release archive.check(also run on its own): fails when engines.json pins another rhwp version than the script builds.
The Linux package jobs pass the result to build/build-portable.ps1 -RhwpOverride <dir>. Local builds without it use
the downloaded rhwp (fine on the machine that builds it). SharpHook's native libraries are packaged as they come
from NuGet: SharpHook 8.0.0 built its X11 back ends against glibc 2.38, 8.0.1 needs glibc 2.34 at most again.
build/check-elf-glibc.py lists the newest GLIBC version every ELF file in the package needs and fails above 2.35,
and the packages are unpacked and started on Ubuntu 22.04 (build/smoke-portable.ps1: CLI, rhwp conversions, loading
the X11 input hook back end; build/smoke-launch-app.py: the app under Xvfb). If a SharpHook or engine update fails
these checks, keep the previous version until upstream builds for an older glibc again, or build the binary from its
pinned source in build/build-linux-native.py like rhwp (PR #113 did this for SharpHook 8.0.0's libuiohook back ends).
- Animations. An animated GIF, WebP, APNG or MNG keeps every frame and its timing when converted to one of these (animated GIF ↔ animated WebP ↔ APNG ↔ MNG, WebP lossy or lossless as the preset says) and becomes a multi-page TIFF; other targets get the first frame. Multi-page TIFFs and PDFs stay one image per page in WebP.
- APNG (
Magick/Apng.cs) is Filee's own code, written from the APNG specification: ImageMagick reads only an APNG's default image and writes APNG through an external FFmpeg. Every frame is an ordinary PNG decoded or encoded by ImageMagick; Filee handles the chunks around them.- Reading composes each frame onto the canvas as a player shows it:
fcTLregions and offsets, blendSOURCE/OVER, disposeNONE/BACKGROUND/PREVIOUS, delays (a denominator of 0 means hundredths) and the loop count. A default image that comes before the firstfcTLis not part of the animation and is skipped. Animated PNGs saved as.pngare recognised by theiracTLchunk and stay animated as GIF, WebP or APNG; other targets get the first frame. - Writing gives every frame the same pixel format (8-bit RGB, or RGBA when any frame is transparent or smaller than
the canvas): the first frame is the default image (
IDAT, so viewers without APNG support show it), the others arefdATchunks holding only the rectangle that changed (disposeNONE, blendSOURCE); a frame identical to the one before lengthens its delay instead. Checked with FFmpeg's APNG demuxer. - APNG ↔ video needs FFmpeg: video → APNG uses its
apngencoder and muxer (24-bit RGB, looping, the GIF's frame rate and width limits), APNG → MP4/WEBM/MOV its APNG demuxer. Animated WebP and GIF still reach video through GIF, a still picture through PNG.
- Reading composes each frame onto the canvas as a player shows it:
- More raster formats through ImageMagick's own coders, each checked against
MagickNET.SupportedFormatsand converted there and back inImageFormatTests: QOI, MNG (animated), DDS (DXT block compression, which is lossy; of a texture with mipmaps the largest image is read), PCX, CUR (at most 256 × 256, larger pictures are scaled down), XPM, XBM and WBMP (1-bit), PAM, PFM, SGI (.sgi,.rgb,.rgba,.bw, always read as SGI; ImageMagick would take.rgbfor headerless pixels), Sun Raster (.ras,.sun) and MIFF. XBM, WBMP, PFM, PCX and Sun Raster are written without transparency (flattened onto the preset's background): PFM has none, and the alpha planes ImageMagick can put into PCX and Sun Raster are read by hardly any other program. Not offered: PICT, because ImageMagick can't read back direct-colour PICTs wider than about 130 pixels that it wrote itself. - Read only: Photo CD (the 768 × 512 base image), OpenEXR and Radiance HDR. The bundled Magick.NET is the 8-bit (Q8) build without HDRI, so high-dynamic-range values are clipped to the displayable range and stored with 8 bits per channel; there is no tone mapping. Writing EXR or HDR from 8-bit pictures would only make larger files, so they are not targets.
- DICOM (
.dcm,.dicom, read only). The Q8 build reads only 8-bit DICOM, while CT, MR and X-ray scanners store 12 or 16 bits, soMagick/Dicom.cs(written from the DICOM standard, PS3.5 and PS3.3 C.7.6.3 / C.11.2) reads uncompressed files itself: implicit and explicit VR little endian, explicit VR big endian and deflated files, with or without the 128-byte preamble; 8 or 16 bits allocated, signed or unsigned; MONOCHROME1 (inverted) and MONOCHROME2, RGB and YBR_FULL (interleaved or planar); every frame of a multi-frame file. Grey values go through the rescale slope / intercept and the file's first window (centre / width) like a viewer's default view, or are stretched over the file's range when there is no window, to 8 bits. Compressed pixel data (JPEG, JPEG-LS, JPEG 2000, RLE) is left to ImageMagick, which manages only 8-bit images; others fail with a message asking for an uncompressed export. - Fujifilm HEIF (
.hif) is read as HEIC. - Rotate and flip happen after the EXIF orientation is applied, in the one step of a route that turns images
(
IImageTransformer). There is no lossless JPEG rotation: a turned JPEG is re-encoded, at its own estimated quality when that is lower than the preset's, so a phone photo saved at 75 doesn't come back bigger at 95. - Layered files. PSD/PSB convert from Photoshop's composite (ImageMagick merges the layers when a file was saved without "maximize compatibility"). GIMP XCF layers are composited at their offsets with their blend modes; hidden layers are left out. ImageMagick reads 8-bit XCF without zlib tile compression (GIMP 2.10+ "better but slower compression" files give a clear error).
- Camera RAW (DNG, CR2, CR3, NEF, ARW, …, and
.raw) is decoded by LibRaw with the camera's white balance, sRGB output and AHD demosaicing, then turned upright from the EXIF orientation. - EMF/WMF are rendered through GDI+ (Windows only) at the preset's render DPI (at least 150), keeping their physical size in PDF output.
- SVG → PDF replays the drawing into Skia's PDF backend: paths, gradients and clips stay vector. Text is written as glyph outlines (shaped with HarfBuzz), because SkiaSharp's PDF backend has no font subsetter and would embed every font whole (13 MB for one line of Korean). SVG → images is rendered at its final size: the preset's DPI, at least 1024 px on the long edge, or exactly the preset's resize target. External files next to the SVG are loaded; nothing is fetched from the network and scripts don't run.
- ICNS files are written with every size iconutil makes (16–1024 px, PNG), the picture centred on a transparent square; reading takes the largest image (PNG, JPEG 2000 or the old RLE + mask entries).
- Illustrator files saved with "Create PDF compatible file" (the default since Illustrator 9) are PDFs and need nothing extra; PostScript-based ones need Ghostscript.
- EPS and PS → PNG go straight through Ghostscript's
pngalphadevice, so the background stays transparent; JPG, WebP, … and AI reach images through PDF (Ghostscript → PDFium), and SVG reaches EPS through PDF.
Built in (Vector/Tracing), no Potrace or other program: Filee's own code, written from published descriptions of the
steps (no code of Potrace, AutoTrace or VTracer is used). Every raster format ImageMagick reads (PNG, JPG, BMP, GIF,
WEBP, TIFF, HEIC, PSD, RAW, DICOM, …) is traced, and other raster images through PNG (ICNS, APNG). Only raster images are traced:
PDF, EPS, PS, AI, EMF/WMF, CDR, documents and videos are never redrawn from a rendering, so without a real vector
route they have no SVG target (PDF → SVG, for example, needs LibreOffice). The first frame or page is used, turned upright, in sRGB. The SVG has the picture's pixel size
(width/height/viewBox in pixels), so it lines up with the original.
- Black and white (Line art to SVG: logos, signatures, scans): pixels darker than Otsu's threshold are ink
(transparent pixels count as white paper). On a transparent background the opaque pixels are the ink, or only their
darker part when the opaque area itself is light and dark. One black
<path>on a transparent background. - Colour (Trace to SVG: illustrations, icons): k-means reduces the picture to the preset's number of colours
(2–64; run on the mean colours of 32 768 colour boxes weighted by their pixel counts, so it sees every pixel). One-pixel
bands of in-between colours along edges (anti-aliasing) join the colour on either side; regions smaller than a few
pixels (more on large pictures) join the neighbour they share most border with. Colours are stacked largest first,
and each colour's shape is the union of itself and every colour above it: lower shapes run under upper ones, so no
background shows through between neighbouring shapes. Transparent areas stay transparent. One
<path>per colour. - Outlines: each shape's boundary is followed along the pixel edges (inside always on the right, so holes come out reversed and fill correctly with the non-zero rule; pixels touching only at a corner stay one shape). Outlines smaller than the speck size are dropped (and small holes filled).
- Curves: the staircase is replaced by points on the edge it approximates (midpoints of the steps); corners are kept where two long pixel runs meet, at the ends of thin lines, where the direction turns sharply within two pixels, or where a gentler turn happens all at once (unlike a small circle). Points between corners are smoothed; stretches that stay within half a pixel of a line without bending become lines; the rest becomes cubic Béziers fitted by least squares (Philip J. Schneider, "An Algorithm for Automatically Fitting Digitized Curves", Graphics Gems, 1990), meeting lines and each other with matching tangents.
- Options (Settings → Presets): mode, colours (colour mode) and Detail 0–100. Low detail drops larger specks, smooths more and allows the curves 1.15 px of deviation; high detail keeps every pixel's shape (0.25 px). The default 50 drops specks under 6 px (black and white) or merges regions under 17 px (colour), at 0.7 px; speck and region sizes grow with pictures larger than 1000 px (up to four times).
- Size and speed: pictures over 6 megapixels or 4096 px on a side are reduced before tracing; the SVG keeps the original size and says so in a comment. A 2000 × 2000 illustration traces in 1–3 s at 16 colours and well under a second in black and white; a noisy 2000 × 2000 photo at 16 colours takes about 4 s (64 colours about 5 s, and a 20 MB SVG). Photos are not what tracing is for: they come out as posterized patches.
- Routes: tracing is the only raster → SVG route and costs 40 (
VectorConverter.TraceCost), more than any route from an image to a format SVG leads to, so it is never a step on the way to PDF, PNG, EPS, …; a test checks every pair of formats. Its edges are markedImageSourcesOnly: the planner uses them only when the file being converted is a raster image (FormatCategory.Image), so a vector file rendered to PNG can't be traced; a test checks this for every pair of formats too. Through SVG, pictures reach DXF and DWG.
Vector/SvgDxfWriter.cs writes the drawing's outlines with ACadSharp (DXF, AutoCAD 2018 like the CAD engine; DWG is
DXF → DWG by the CAD engine). The SVG is loaded with Svg.Skia as for PDF output, so groups, transforms, <use>,
nested <svg>, CSS and text are resolved the same way.
- Circles and ellipses become CIRCLE and ELLIPSE (also rotated, scaled or skewed: the transformed axes come from the singular values of the transform). Paths, lines, polylines, polygons and rectangles (rounded too) become LWPOLYLINEs, closed where the shape is closed, or a LINE for one straight segment. Bézier curves and arcs are divided into straight pieces within 0.01 mm of the curve: every CAD and CAM program reads polylines, while SPLINE support varies. Text becomes its glyph outlines.
- Units: SVG user units are CSS pixels, 96 per inch, so 1 px = 25.4 / 96 mm and the DXF is in millimetres
(
$INSUNITS4, metric): a drawing 100 mm wide in the SVG is 100 mm wide in CAD. The y axis is flipped (SVG points down, DXF up) with the viewport's bottom-left corner at (0, 0). - Colours are kept as true colours (the stroke's, else the fill's; black for gradients). Fills, stroke widths, clipping, masks, images and filters are not represented; a shape both filled and stroked is written once. An SVG with nothing to outline fails with a message.
Filee writes OWPML (HWPX) itself, so saving as HWPX needs no Hancom Office and shows no approval dialog.
Readers turn the source into a small document model (Hwp/Hwpx/HwpxModel.cs) and HwpxWriter writes the package:
- DOCX is read directly (
Hwp/Hwpx/Docx/DocxReader*.cs) so the layout survives:- page size, orientation and margins per section; columns, also when they change mid-page;
- headers and footers (odd/even pages, hidden on the first page) with page numbers and page counts;
- paragraph formatting (alignment, indents, spacing, line spacing, tab stops, keep with next);
- character formatting (fonts incl. theme fonts, size, colour, highlight and shading, bold/italic/underline/strike, super/subscript, spacing);
- lists with Word's numbering formats (1., 가., ①, i., bullets);
- tables with merged cells, column widths, borders and shading from the table style and cells, repeated header rows;
- pictures inline and floating, text boxes, rectangles/ellipses/lines with solid or gradient fills, groups;
- footnotes, endnotes, bookmarks, hyperlinks (web and within the document, e.g. a table of contents);
- Word's document grid ("lines"), which 한글 does not have, as an "at least" line spacing.
- XLSX, XLS, ODS, CSV and TSV (
Office/Sheets, see Spreadsheets): every visible sheet becomes a section with one table. Cells show what Excel shows (number formats via ExcelNumberFormat, dates, percentages, cached formula results), with fonts, fills, borders, alignment and merged cells; frozen top rows repeat on every page. The page is A4 with narrow margins, landscape when the columns are much wider than portrait, and the table is scaled to the page width like Excel's "Fit all columns on one page". CSV is read with its delimiter (comma, semicolon, tab) and encoding (UTF-8 / UTF-16 with BOM, else CP949 as saved by Korean Excel). Not converted: charts, pictures, conditional formatting, print areas and page breaks. - PPTX (
Hwp/Hwpx/Pptx): one page per slide, sized like the slide, with every object floating at its place: placeholders inherit position and text style from the layout and master, text with bullets and numbering, shapes (rectangles, rounded rectangles, ellipses, lines) with solid or gradient fills and outlines, pictures, tables with PowerPoint's default table style, groups, master decorations and the slide background. Not converted: charts, SmartArt, rotation, effects (shadows, glow), picture cropping, animations, notes and hidden slides. - TXT needs no reader (one paragraph per line; UTF-8, UTF-16 or the Korean code page CP949).
- Markdown is parsed with Markdig (
MarkdownReader.cs): headings, emphasis, code, lists (1. / a. / i. with start numbers), task lists, quotes, tables with spans, links, images, footnotes and math. No Pandoc needed. - PDF is read with PdfPig (see PDF reader below); DOCM / DOTX / DOTM, XLSM / XLTX and PPTM / POTX / PPSX are read like DOCX, XLSX and PPTX.
- HTML is parsed with AngleSharp (
HtmlReader.cs,HtmlCss.cs): headings, paragraphs, bold / italic / underline / strike / sub / sup / code / mark, the basic inline CSS (colour, background, font weight, style and size, text-align, text-indent, page breaks) and simpletag/.classrules of style sheets, nested lists with start numbers and types, tables with spans / header rows / borders, pictures (local files and data: URIs; remote pictures are skipped), links and anchors, quotes,pre,hrand figures. Scripts, styles, forms andnavare skipped. The encoding comes from the BOM, the XML declaration or<meta charset>, else UTF-8 or the system code page. No Pandoc needed. - ODT, RTF are parsed by Pandoc into its JSON AST (
PandocAstReader.cs, following pypandoc-hwpx). - Markdown, HTML and Pandoc keep structure only, so page setup comes from the built-in template (A4).
Element order and attribute values follow files saved by 한글 where the schema and 한글 disagree, for example:
hp:defaultrepeats paragraph margins and fixed line spacing doubled (hp:caseholds the real values);- page width/height are always those of the portrait sheet,
landscape="NARROWLY"turns it; - 한글 keeps a hanging paragraph's first line at
leftand indents the other lines; - object offsets are unsigned: objects reaching into the margin are anchored to the paper instead;
- layout caches (table row heights, inline tab widths) are estimated, because simpler readers use them as-is.
Every generated file is checked in the tests with three independent readers: rhwp (renders it), Unhwp (reads the
text) and LibreOffice + H2Orestart (opens it), see tests/Filee.Engines.Tests/DocxToHwpxTests.cs and
OfficeTests.cs.
Not converted: Word charts, SmartArt, equations (kept as text), free-form shapes, tracked changes and comments.
SpreadsheetConverter (engine id spreadsheet) converts between XLSX, XLS (and XLT templates, read only), ODS, CSV and TSV (also
.tab) in-process; every format goes through one in-memory workbook (Office/Sheets/Workbook.cs) whose cells keep
the text the spreadsheet shows, the value behind it and its Excel number format.
| Format | Read | Written |
|---|---|---|
| XLSX | own reader: values, number formats, fonts, fills, borders, alignment, merges, widths, heights, hidden rows / columns / sheets, frozen rows | every sheet, numbers and dates as numbers with their formats, booleans, styles, merges, widths, heights, hidden rows / columns, frozen rows |
| XLS (Excel 97–2003, also Excel 5/95) | ExcelDataReader (MIT): values, number formats, merges, horizontal / vertical alignment; widths, heights and hidden rows / columns from the BIFF records (XlsLayout.cs) |
no (LibreOffice) |
| ODS | own reader (content.xml, styles.xml, settings.xml): repeated rows / columns (the empty repeats up to the end of the sheet are skipped), spans, every value type, the text Calc shows (text:p), data styles as Excel codes, cell styles, widths, heights, hidden rows / columns / sheets, frozen and header rows |
every sheet, typed cells (float, percentage, currency, date, time, boolean) with data styles and the shown text, styles, merges, widths, heights, hidden rows / columns, frozen rows |
| CSV / TSV | delimiter (CSV: comma, semicolon or tab; TSV: tab) and encoding (UTF-8 / UTF-16 with BOM, else CP949) | UTF-8 with BOM, CRLF, one file per sheet |
- Number formats: Excel codes and OpenDocument data styles are converted both ways (
OdsNumberStyles.cs) for digits, decimals, grouping, thousands scaling, percent, scientific, currency symbols, literal text, colours, positive / negative / zero sections, dates, times and durations. Codes with conditions ([>100]), fractions or text between digits are written as plain numbers (General). - Column widths use Excel's unit (a digit of Calibri 11 = 7 px); in ODS a character is 5.25 pt. LibreOffice measures with its own fonts, so a width can differ by up to a fifth after LibreOffice opens the file.
- XLS cells keep the default font, fill and borders (ExcelDataReader does not expose them); XLS output still needs LibreOffice. Password-protected XLS and ODS files stop with a clear message.
- Not converted: formulas (their cached results are kept), charts, pictures, comments, conditional formatting, data validation, print areas and page breaks.
Fonts are converted in-process (Filee.Engines/Fonts); zlib and Brotli come with .NET. The input format is
recognised by content, so a mislabelled file still converts.
- WOFF 1.0: every table zlib-compressed (stored as-is when that isn't smaller). Lossless both ways.
- WOFF2: one Brotli stream at the highest quality with the glyf/loca transform and, where it applies, the hmtx transform, so files come out the size of Google's reference encoder's. Reading handles both transforms, the overlap bitmap, untransformed tables and the first font of a WOFF2 collection. Like other encoders, Filee drops the DSIG signature and sets bit 11 of head.flags. Brotli is slow on big fonts (about 10 s per MB, so about a minute for a 4.6 MB CJK font); the job shows progress and can be cancelled.
- EOT: written as version 0x00020001 (names and OS/2 fields in the header, no MicroType Express compression, no XOR obfuscation, no URL restriction). Uncompressed EOT files are read (XOR-obfuscated ones too); MTX-compressed ones fail with a clear message.
- TTF → OTF keeps the TrueType outlines: TrueType-flavoured OpenType is valid OpenType and what systems and browsers expect; cubic outlines would gain nothing.
- OTF with CFF outlines → TTF (and → EOT, because Internet Explorer renders only TrueType outlines in EOT) converts the glyphs: Type 2 charstrings with subroutines, flex, seac accents and CID-keyed fonts are drawn, curves are approximated with quadratic splines within 1/1000 em (the approach of fontTools' cu2qu) and contours are reversed to TrueType's direction. glyf, loca, maxp 1.0 and post (format 2 with the CFF glyph names, format 3 for CID fonts) are built; head, hhea, hmtx and, from VORG, vmtx are updated; CFF, VORG and DSIG are dropped; every other table (cmap, name, OS/2, GSUB, GPOS, kern, ...) is kept. PostScript hints are not converted: the font gets a gasp table (smoothing at every size) and a prep that switches on dropout control instead.
- Not supported: CFF2 (variable) outlines to TTF or EOT (to WOFF, WOFF2 and OTF they convert), writing font collections, and WOFF/WOFF2 extended metadata (dropped).
tests/Filee.Engines.Tests/FontTests.cs checks every conversion with small OFL fonts in
tests/Filee.Engines.Tests/Fonts (see OFL.txt there): round trips keep every table byte for byte, a WOFF2 made
by Google's encoder decodes to the source font, and converted CFF glyphs are compared with the originals in Skia.
The counterpart of the HWPX writer: Hwp/Hwpx/Docx/DocxWriter*.cs writes WordprocessingML by hand from the same
document model, so every reader in DocumentReaders can produce an editable Word file without Word, LibreOffice or
Pandoc. It writes:
- sections with page size, orientation, margins (header and footer distances), columns, page number restarts; columns that change mid-page become continuous section breaks, as Word does it;
- headers and footers (odd/even pages, "different first page") with PAGE / NUMPAGES fields; a section without its own header gets an empty one, since Word would otherwise repeat the previous section's;
- paragraphs: alignment, indents (first line / hanging), spacing, line spacing (the model's percent of the font size is converted back to Word's multiple, the inverse of DocxReader), keep with next / keep lines / widow control, tab stops with leaders, page and column breaks;
- runs: Latin and East Asian fonts, size, bold, italic, underline, strike, colour, highlight (Word's named colours) or shading, super/subscript, letter spacing;
- headings as Word's built-in "heading 1".."heading 9" styles (outline levels, so the navigation pane and tables of contents work), lists as real numbering definitions (1., a., 가., ①, bullets, start numbers, nine levels);
- tables with column widths, horizontal and vertical merges, borders, fills, cell alignment, row heights and repeated header rows;
- pictures inline and floating (anchored to page, margin, column or paragraph, with wrapping), text boxes and rectangles / rounded rectangles / ellipses / lines as Word 2010 shapes (wps) with solid or gradient fills and outlines, hyperlinks (web and to bookmarks), bookmarks, footnotes and endnotes.
Parts, relationships and content types are written per part (pictures and links in headers and notes are related
from their own part), children in schema order. The tests validate every file with the Open XML SDK schema
validator, read it back with DocxReader (DOCX → model → DOCX → model keeps the layout) and convert it with
LibreOffice as an independent reader, see tests/Filee.Engines.Tests/DocxWriterTests.cs.
Its edges cost 8, less than Pandoc's and LibreOffice's direct conversions, so Markdown → DOCX, PDF → DOCX, ... use it. DOCX → DOCX is no edge; the Word variants are the Office Open XML engine's job.
Pdf/PdfDocumentReader*.cs reads PDFs with PdfPig (Apache-2.0) into the document model, for PDF → DOCX, HWPX and
(with PdfTextConverter) TXT:
- words with their positions, fonts, sizes and colours → lines → paragraphs: a line continues the paragraph above when it follows at about one line pitch, unless it starts with a bullet or number, is indented as a first line, or the previous line ended although the next word would have fitted; hyphenated line ends are joined, and no space is added between Chinese or Japanese lines;
- tab stops: a line split at wide gaps ("1. RFP Published 08/14/2026") stays one paragraph with tabs and left tab stops where its parts start, unless a column gutter lies between them; a gap counts as a tab after a list number, when it is wider than 6 em, or when a row right above or below has a tab at the same place (a form's value column);
- line breaks: in a paragraph narrower than its column (an address block), a line that ended although the next line's first word would have fitted keeps its line break;
- reading order: gutters between columns are found where no narrow paragraph crosses; paragraphs that span the columns (titles, figures, closing text) split the page into bands, read column by column;
- headings from font sizes (short paragraphs clearly larger than the body text, the largest size is level 1), bold and italic from the fonts, colours, links from link annotations, alignment, indents, spacing and line pitch;
- pictures: JPEG streams are kept as they are, others are decoded to PNG, and what PdfPig cannot decode is cut out of the rendered page; a picture covering the page behind text (the scan under an OCR layer) is skipped;
- page size per page and margins shared by the document; running headers and footers that repeat on most pages (and bare page numbers) are left out;
- a page without text (a scan, a page of drawings, a turned page) becomes the rendered page (PDFium, 150 dpi JPEG) as a picture behind the text of an empty page, so nothing is lost; there is no OCR. PDF → TXT includes the invisible OCR text layer of scanned PDFs that have one.
Password-protected PDFs fail with a clear message. Not converted: tables (their cells come out as paragraphs in reading order), vector drawings on text pages, form fields and annotations other than links.
Pdf/PdfSlidesConverter.cs turns a PDF into a PowerPoint deck of pictures: every page is rendered (PDFium, as for
PDF → images) and becomes one picture on its own slide. The slides look exactly like the PDF but are not
editable: there are no text boxes to change, the PDF's layout is not rebuilt. PDF → DOCX is the editable route.
- Slide size is the first page's own size (an A4 portrait PDF gives A4 portrait slides, a PDF exported from a 16:9 deck gives 16:9 slides), within PowerPoint's limits of 1 to 56 inches a side. Every page, also one of another size or orientation (pages turned in the PDF are shown turned), is fitted onto the slide without cropping and centred on white.
- Resolution is the preset's render DPI, at least 150 and at most 600; a page larger than 25 megapixels at that resolution (a poster) is rendered at a lower one. The preset's page range picks the pages.
- Pictures are JPEG at the preset's quality (90 by default, as the "PPTX (slides)" preset sets it). Pages are rendered on white, as PDF viewers show them, so they have no transparency that would need PNG, and JPEG keeps a long deck at a size that is easy to send (an A4 text page at 150 dpi is a few hundred KB).
- Speaker notes hold the page's text, read like PDF → TXT (reading order, the invisible OCR layer of scans included), so it can still be searched and copied. Pages without text get no notes; when PdfPig cannot read a PDF that PDFium renders, the deck is written without notes.
- The package (
Pdf/PictureDeckWriter.cs) is written by hand: presentation, one slide master with a blank layout, a theme, the slides with their pictures, a notes master and notes slides, and the document properties. The tests validate it with the Open XML SDK and read it back with Filee's own PPTX reader. - The edge is
DirectOnly: the route planner uses it only for a PDF converted to PPTX. A Word document or an image is never turned into picture slides through PDF, and a picture deck is never a step towards ODP, PPSX or another format (a test checks that every other pair of formats keeps its route).
Password-protected PDFs fail with the same message as PDF → DOCX.
"Compress PDF" (and any PDF → PDF preset with Compress set, also with a page range or one file per page) makes a
PDF smaller while it stays a PDF, for e-mail attachments and upload limits. Pdf/PdfCompressor.cs does it with
PDFsharp and Magick.NET; nothing has to be installed.
| Level | Pictures scaled down to | JPEG quality |
|---|---|---|
Smallest (Screen) |
72 dpi | 55 |
Balanced (Ebook, the preset's) |
150 dpi | 65 |
High quality (Print) |
300 dpi | 85 |
- Pictures:
Pdf/PdfPictureUses.csfollows the content streams of the pages, of the forms they draw and of annotation appearances (q/Q/cm/Do) to find the largest size each picture is drawn at. A picture drawn at more than 1.5 × the level's resolution is scaled down to it (as Ghostscript does); a picture whose drawing can't be followed is taken as large as the page holds it, so it is never scaled down too far. Photos are saved as JPEG at the level's quality, pictures with up to 256 colours (charts, screenshots, logos) losslessly (Flate with PNG prediction). A JPEG that keeps its size is encoded again only when that saves at least 10 %. A picture is replaced only when the result is smaller. - Which pictures: 8 bits per component, gray, RGB (also ICC-based and calibrated) and CMYK, stored as JPEG, Flate (with or without PNG prediction), RunLength or uncompressed. CMYK JPEGs, 1-bit pictures and masks, indexed (palette) pictures, spot colours, JPEG 2000, JBIG2, CCITT and pictures with a colour-key mask stay as they are.
- Transparency: a soft mask is scaled with its picture and kept lossless, so transparent parts stay transparent.
- Duplicates: a picture stored more than once with the same data (a logo embedded again on every page) is kept once.
- Everything else: every stream is deflated at the highest level when that's smaller; page thumbnails and other
applications' private data (
PieceInfo, such as Illustrator's editing data) are removed, as are unused objects. When the preset doesn't keep metadata, the XMP metadata of pages and pictures goes too. The document's own XMP is written afresh from its document information by PDFsharp, as for every PDF it saves. - Unchanged: text, fonts, vector drawings, links, bookmarks and form fields. The whole file is compressed as it is; a page range or single pages are first copied into new PDFs, as without compression (which leaves bookmarks and form fields behind), and so is a PDF locked only against changes.
- Never bigger: when nothing gets smaller (a PDF of text only, one compressed before), the PDF is written as it was
and the toast (and
filee convert) says that it couldn't be made smaller. Password-protected PDFs fail with the same message as the other PDF conversions. - Ghostscript (optional, Also try Ghostscript in the preset): when it is installed, the PDF is also run through
pdfwritewith-dPDFSETTINGS=/screen,/ebookor/printer(the same 72, 150 and 300 dpi) and-dCompatibilityLevel=1.6, and the smallest of the two results is kept; when Ghostscript fails, the built-in result stands. It is off by default: Ghostscript rewrites the whole file (fonts are subset again, structure and form fields may change), so it is the user's choice. It also writes object streams, which PDFsharp can't, and its JPEGs are somewhat smaller at the Balanced level. - Not done: object streams (PDFsharp writes none), font subsetting or removal of unused glyphs, converting colour to gray, flattening transparency or form fields, recompressing JBIG2/CCITT scans.
Office/OoxmlConverter.cs converts between the variants of a family (Word, Excel, PowerPoint) by copying the
package with the main part's content type rewritten. Going to a macro-free type also removes the VBA project, its
signatures and data, Word's key map customisations and Excel 4.0 macro sheets (with their sheet entries), and every
relationship and content type entry pointing to them. The built-in readers take the variants directly (DOCM → HWPX,
XLSM → CSV, PPSX → DOCX ...).
Email/EmlConverter.cs reads .eml files with MimeKitLite (MIT), which decodes quoted-printable and base64, encoded
headers (RFC 2047 / 2231) and charsets including EUC-KR / CP949 and ISO-2022-KR; raw 8-bit header text that is not
UTF-8 is read as CP949, as older Korean mail programs wrote it.
Outlook .msg files are read with MsgReader (MIT, Email/OutlookMessage.cs) and rebuilt as a MIME message with
MimeKit: sender, To / Cc / Bcc, subject, sent date, Message-ID, the text and HTML bodies (MsgReader takes the HTML out
of RTF-only messages), attachments, pictures the HTML shows (cid:, kept with the body) and attached Outlook items
(as attached .eml messages). That message then goes the EML way below, so MSG → HTML / TXT / ZIP are direct edges
(cost 10; through EML would be 20 and parse twice) and MSG → EML writes the rebuilt message. Attachments that only
link to a file elsewhere (ATTACH_BY_REFERENCE) are not followed. MsgReader brings OpenMcdf (MPL-2.0, the compound
file reader), RtfPipe (MIT) and UTF.Unknown (MPL-1.1, charset detection for old ANSI messages).
- EML → HTML: a standalone page with From / To / Cc / Date / Subject / Attachments and the HTML body (its style
sheets kept, scripts removed) or the text body; pictures sent inline (
cid:) become data URIs. - EML → TXT: the same headers and the text body (or the HTML body as text).
- EML → ZIP: the attachments (attached messages as .eml) with safe, unique file names.
Data/DataConverter.cs (engine id data) reads every format into one tree (Data/DataTree.cs: objects with their
keys in file order, arrays, text, true / false, integers of any size, floats, TOML dates and null) and writes it
again, so structure and types carry over as far as the target can hold them. Output is UTF-8 without BOM with LF
line ends, indented by two spaces (CSV: like the spreadsheet engine, UTF-8 with BOM and CRLF for Excel). There are no
options: the output is always pretty-printed.
| Format | Library | Read | Written / lost |
|---|---|---|---|
JSON (.json) |
System.Text.Json | comments and trailing commas allowed (JSONC) | non-ASCII text as it is; NaN / infinities as text ("NaN") |
JSON Lines (.jsonl, .ndjson) |
System.Text.Json | one value per line, blank lines skipped, read as an array | one compact line per record (the array, or the array inside wrappers, see CSV) |
YAML (.yaml, .yml) |
YamlDotNet (MIT) | YAML 1.2 core schema: unquoted true, null / ~, numbers (0x, 0o, .inf, .nan) are typed, the rest and anything quoted is text (yes / no stay text); explicit !!str / !!int … tags; anchors and aliases resolved (copied), merge keys (<<) applied; several documents (---) become an array; comments dropped; alias "bombs" (more than 5 million values) and nesting deeper than 256 refused |
block style; text that would read back as another type ("true", "007", "", YAML 1.1's yes / on, dates) is quoted; multi-line text as a literal block (|) |
TOML (.toml) |
Tomlyn (BSD-2-Clause, TOML 1.1) | dates and times keep their TOML spelling (text in JSON / YAML) | written by Filee: plain keys, then [tables] and [[arrays of tables]]; null values are left out (TOML has none); a root that is not an object goes under items ([[items]]); integers beyond 64 bits as text |
XML (.xml) |
System.Xml | root element = an object with one key; attributes as @name, text next to attributes or children as #text, repeated elements as an array (one child is no array), <a/> → null, <a></a> → empty text, CDATA as text; all values are text (XML has no types); comments, processing instructions and the DTD dropped, external entities never loaded; the encoding from the declaration (EUC-KR …) |
a root that is not one object with one key goes into <root>, items of nested arrays into <item>; names that are not XML names are escaped (first name → first_x0020_name, unescaped when read back); x:name keeps its namespace when @xmlns:x declares it; control characters XML 1.0 can't hold are removed |
CSV (.csv) |
Filee (Office/Sheets/CsvFormat.cs) |
first row = keys, one object per row; plain numbers (not 007) and true / false typed, the rest text; delimiter and encoding detected as for spreadsheets |
only tables: an array of objects (columns = every key, first seen first; an object inside a row becomes parent.child columns, one level only), an object of arrays (columns), an array of arrays (rows) or one flat object (one row); objects that only wrap the array ({"users": […]}, <rows><row/>…</rows>) are looked through. Anything else stops with a message saying what is not a table |
.csv and .xml ownership. CSV stays a spreadsheet format: the spreadsheets donut, XLSX / ODS routes and
"Merge into PDF" are unchanged, and the data engine only adds edges csv ↔ json / jsonl / yaml / toml / xml. Routes
combine: XLSX → CSV → JSON, YAML → CSV → XLSX, vCard → CSV → JSON. The spreadsheets donut gets "JSON". .xml had no
owner before; XML-based formats Filee converts have their own extensions (.svg, .fb2, .dbk, .opml …), so a
plain .xml file is treated as data and opens the data donut.
vCard (Data/VCard.cs, own parser): vCard 2.1, 3.0 and 4.0 → CSV with the columns Full name, Given name, Family
name, Organization, Title, Email, Phone, Address, Birthday, Note (one row per contact; several values joined with
; , each with its kind in brackets: 010-1234-5678 (mobile); 02-123-4567 (work)). Folded lines, quoted-printable
with soft line breaks, CHARSET parameters, escapes, groups (item1.EMAIL), 2.1's bare types (TEL;CELL) and 4.0's
quoted type lists and tel: URIs are read; a value without CHARSET is UTF-8 when valid, else CP949 (address books
of older Korean phones); UTF-16 files with a BOM are read too. Pictures and nested cards (2.1 AGENT) are skipped,
a missing full name is made from the name parts (홍길동, "Ada Lovelace"). CSV → vCard reads those columns (also
Name, Company, Mobile, E-mail … and Korean / Chinese headers such as 이름, 전화번호, 이메일, 姓名, 电话) and writes
vCard 3.0 in UTF-8, lines folded at 75 bytes; an address goes into the street field.
Built in, no AutoCAD or ODA software (Filee.Engines/Cad). ACadSharp reads
and writes the files; Filee's own renderer draws them with SkiaSharp (one drawing routine for PDF, SVG and images).
- Reading: DWG from R14 (AC1014) to the AutoCAD 2018 format (AC1032, still current in AutoCAD 2026); DXF R12 to
2018, ASCII and binary. DWG R13 and older fail with "version not supported". Korean AutoCAD writes pre-2007 DXF with
the code page name
ANSI_949, which ACadSharp 3.8 does not map; Filee rewrites that header in memory so the text is decoded as CP949 (CadFile.OpenDxf, covered by a test that fails once ACadSharp handles it). - Writing (DWG ↔ DXF): always AutoCAD 2018 (AC1032) — the current DWG format (AutoCAD 2018 and later, DWG TrueView, BricsCAD, ODA tools); 2018 DXF stores text as UTF-8. ACadSharp's DWG writer is younger than its reader and its DXF writer, so DXF is the safer format to hand on. Objects ACadSharp does not model (proxy and AEC objects, some 3D and underlay data) are not written; degenerate dimensions it cannot measure are left out of DWG output instead of failing the file.
- What is drawn: model space seen from the top (Z is dropped). If model space has nothing visible, the active
layout (paper space) is drawn instead; viewports inside it are not followed. The page is fitted to the extents of
what is actually drawn — the header's EXTMIN/EXTMAX are ignored, so drawings far from the origin still fill it.
- PDF: an ISO A3 sheet (A4 or Letter when the preset asks for it), landscape when the drawing is wider than tall, 10 mm margins (or the preset's margin). Vector paths, text as text with embedded fonts.
- SVG: the drawing's aspect ratio with a 420 mm (A3) long edge at 96 px/inch, white background, text as
<text>. - Images: the same page rendered at the preset's render resolution (150 dpi → 2480 px on the long edge, at most 12 000 px / 80 MP), then the preset's image options (resize, grayscale, quality, DPI) as for other images.
- Entities: LINE, XLINE/RAY (clipped to the page), LWPOLYLINE and POLYLINE (bulges, constant and tapered widths,
spline-fit and 3D), CIRCLE, ARC, ELLIPSE (also partial), SPLINE (NURBS from control points, knots and weights, or
a smooth curve through fit points), TEXT, ATTRIB and MTEXT (justification, rotation, width factor, oblique,
mirroring; MTEXT word wrap, height/colour/font/width codes, stacked fractions shown as
1/2,%%d%%c%%p,\U+and\M+escapes), INSERT (base point, scale, rotation, MINSERT arrays, nesting, attributes), DIMENSION and TABLE (their anonymous block), LEADER, MULTILEADER (lines, arrows, text or block content), HATCH, SOLID/TRACE, 3DFACE edges, POINT, MLINE, polyface and polygon meshes (edges). - Hatches: solid fills, gradients as a linear gradient, patterns as their real pattern lines clipped to the boundary; a pattern too dense to draw (over 20 000 segments) becomes a light tint of its colour.
- Colours and lines: ACI index table and true colour, BYLAYER / BYBLOCK (inside blocks, entities on layer "0" follow the insert's layer). White and near-white (ACI 7, which AutoCAD shows white on black) draw black on the white page. Line weights are plotted in millimetres ("Default" as a thin 0.18 mm line); line types become dash patterns scaled by LTSCALE, the entity's scale and the block scale (shapes and text inside complex line types are omitted).
- Layers: off, frozen and non-plotting layers (including Defpoints) are not drawn, like a plot. A frozen layer hides a whole block reference; an OFF one only its layer-0 contents.
- Fonts: drawings name SHX fonts, which are not available, so text uses the text style's TrueType font when it is installed (arial.ttf, malgun.ttf ...) and otherwise a sans-serif face (Noto Sans, Arial, Liberation Sans ...). Characters the font lacks fall back per character to a CJK font (Noto Sans KR/CJK, Malgun Gothic, Apple SD Gothic Neo, ...). These come from the fonts SkiaSharp finds on the system; without any, SkiaSharp's default typeface is used and the geometry is unaffected.
- Not drawn (counted per type in the log): 3DSOLID, REGION and BODY (ACIS), IMAGE, OLE objects, PDF/DWF/DGN underlays, WIPEOUT (its masking is not applied), SHAPE, TOLERANCE, proxy entities. MTEXT columns, tabs, underline and overline are ignored.
- Limits: 1 000 000 drawn primitives, block nesting depth 16, 10 000 MINSERT cells, 2 000 000 hatch pattern segments in total; beyond that the drawing is cut short with a warning in the log. Damaged or truncated files fail with "The file is not a valid DWG/DXF drawing or is damaged: …".
Filee.Engines/Models3D converts GLB, glTF, STL, OBJ, 3MF and PLY into each other in managed code: every reader
fills one in-memory model (Model3D: parts with positions, normals, UVs, vertex colours and triangles; materials
with a base colour and a base colour texture) and every writer takes it, so each pair is one step. glTF and GLB are
read and written with SharpGLTF (MIT); STL, OBJ/MTL, PLY and 3MF are Filee's
own code. No native library (Assimp) and no download.
- Units: STL, OBJ and PLY have none; they are read and written as millimetres, as slicers and CAD programs
treat them. 3MF has a
unitattribute (micron to metre) and is written in millimetres. glTF is metres. So a 20 mm STL cube is 0.02 m in GLB and 20 mm again in STL. There is no preset option for another unit. - Axes follow Blender's defaults: STL, 3MF and PLY are Z up, OBJ and glTF Y up, so a part standing on a printer's bed stands upright in a glTF viewer and comes back on its base.
- Structure: node hierarchies, 3MF components and build items are flattened: every mesh instance becomes a part in
world coordinates (one glTF node / OBJ object / 3MF object per part). Polygons are triangulated (ear clipping, so
concave faces are right). OBJ negative indices,
\line continuations and 0–255 vertex colours are understood. - Vertices: binary STL is streamed in blocks and vertices are merged by position (a 1,000,000-triangle STL converts to GLB in a few seconds with about 150 MB of memory). STL normals are not kept: GLB written from STL has none, which glTF viewers must shade flat; STL output gets facet normals from the winding. 3MF output welds vertices by position, so meshes split for normals or UVs stay closed for slicers.
- Colours: vertex colours are kept as sRGB and converted to linear for glTF
COLOR_0; material colours are glTF's linear base colour, OBJ'sKdas it is and 3MF's sRGBdisplaycolor. glTF materials are written metallic-roughness, not metallic, roughness 0.8.
What each format keeps:
| Format | Read | Written |
|---|---|---|
| GLB / glTF | default scene (else all root nodes), triangles, strips and fans, POSITION, NORMAL, TEXCOORD_n of the base colour texture, COLOR_0, base colour factor and texture (metallic-roughness or specular-glossiness), double-sided; .gltf with embedded (data URI) or separate .bin and picture files |
one node and mesh per part, 16- or 32-bit indices, PNG/JPEG textures (others are converted to PNG); .gltf is one self-contained file (its buffer is a base64 data URI, about a third larger than GLB) |
| STL | binary (also with a header starting with "solid") and ASCII (one part per solid) |
binary, one solid |
| OBJ + MTL | o / g / usemtl as parts, v (+ r g b), vt, vn, faces of any size; mtllib with Kd, d / Tr, map_Kd (options skipped) next to the file |
the .mtl and the textures are written next to the .obj (name.obj, name.mtl, name_texture.png, named like the output); vertex colours as v x y z r g b |
| 3MF | objects, components (also in other model parts, as Bambu Studio writes them), build transforms, base materials, colour groups (materials extension); support / other objects skipped |
one object per part, base materials from material colours, a colour group from vertex colours |
| PLY | ASCII, binary little / big endian; vertex position, normal, UV (s/t, u/v, texture_u/texture_v), colours of any type; face lists with per-corner texcoord and face colours; comment TextureFile; other elements skipped |
binary little endian, one mesh; material colours become vertex colours |
Lost on the way (no place in the target): STL keeps only the triangles (no colours, UVs, normals or materials);
PLY has no materials (their colours become vertex colours, textures are dropped); OBJ has no standard vertex
colours (the common v x y z r g b extension is written and read; alpha is lost); 3MF output has no normals, UVs or
textures; and every format drops what Filee's model has no place for: animations, skins, morph targets, cameras,
lights, metallic / roughness / normal / emissive maps, and lines and points. Draco- and meshopt-compressed glTF fail with
a clear message, as do damaged files ("cut short", an index that does not exist, no triangles).
3D → pictures: a small software rasteriser (MeshRenderer) draws an isometric view from the front right, above,
with one light and flat shading per triangle, vertex colours or the texture interpolated across it, and a z-buffer,
at 2048 px and scaled down to 1024 px for smooth edges, on a transparent background. The preset's image options
(resize, background for JPG, quality) apply as for other pictures.
Tests (tests/Filee.Engines.Tests/ModelTests.cs) build a cube, a sphere and a fan of coloured triangles in code and
convert each through every format pair, checking triangle counts, bounds, signed volume (winding), normals, UVs,
colours and materials where both formats keep them; hand-written STL, OBJ, PLY, 3MF and glTF files cover the reading
details, and a generated 1,000,000-triangle STL the speed.
Hwp/Hwpx/HwpxReader*.cs is the inverse of the writer: it reads an HWPX package into the same document model, so
HWPX (and HWP, which rhwp turns into HWPX first) can be written as DOCX, HTML or EPUB without Hancom Office or
LibreOffice. It reads files saved by 한글, by rhwp's export-hwpx and by Filee itself:
- elements are matched by local name (any prefix or namespace version);
hp:switchtakes thehp:casefor HwpUnitChar / 2016 paragraphs, elsehp:default, whose lengths 한글 doubles (as are margins and tab stops outside a switch in older files); every…IDRefis resolved throughheader.xml; - sections in spine order with page size (
landscape="NARROWLY"turns the portrait sheet), margins, gutter, columns (also changing mid-section), hidden first header/footer, start page (startNum,hp:newNum); - headers and footers (both / odd / even pages) with page numbers and counts; an automatic page number
(
hp:pageNum, e.g. "- 1 -" at the bottom centre) becomes a footer or header paragraph; - paragraph shapes (alignment, indents with 한글's hanging-indent convention, spacing, line spacing kinds, tab stops,
keep options, page break before) and complete character shapes (fonts per language, size, bold/italic,
underline, strikeout, colour, shade, highlighter
hp:markpen, super/subscript, letter spacing); - outline paragraphs (개요) as headings, numbered with the section's outline numbering when it shows numbers; numberings and bullets as lists; tabs, line breaks, non-breaking/fixed-width spaces, soft hyphens; tracked deletions are left out;
- hyperlinks (HYPERLINK fields, also across paragraphs;
?name= bookmark), bookmarks, other fields as their text, footnotes and endnotes (without 한글's number), equations as their script text; - tables with spans, column widths, row heights, header rows, borders and fills (
borderFill), cell padding, vertical alignment, captions and nested tables; a table splits the paragraph that holds it; - pictures from
BinData(inline, or floating withhp:posoffsets, alignment and wrapping), rectangles, ellipses and lines with outline and solid / gradient fill, text boxes (hp:drawText), groups.
Not kept (the model has no place for them; skipped and counted by the Read(path, media, skipped) overload):
OLE objects, charts, video, form controls, text art (its text is kept), arcs / polygons / curves without text,
master pages, memos, character ratio, relative size and offset, paragraph borders, picture cropping and rotation.
Password-protected (encrypted) HWPX and DRM-wrapped files stop with a clear error.
The built-in e-book engine (src/Filee.Engines/Ebooks) reads books into the same document model as the HWPX
writer, so every e-book also reaches HWPX, PDF and images. The document model is written back out by
XhtmlWriter (EPUB chapters, HTMLZ, single-page HTML), PlainTextWriter, MarkdownWriter and Fb2Writer.
- EPUB 2 / 3: container.xml → OPF → spine; each chapter is read by the HTML reader and starts a new page, links between chapters become links to bookmarks, pictures come from the package, title / authors / language / cover from the metadata.
- EPUB output is EPUB 3 with an NCX for older readers:
mimetypefirst and stored, a navigation document from the headings, chapters split at level-1 headings and page breaks, one style sheet, pictures in formats every reader shows (others become JPEG / PNG), a cover page for covers the content does not show. - MOBI / AZW / AZW3 / PRC (
Ebooks/Mobi, written from the MobileRead wiki's format description): PalmDB records, MOBI header and EXTH metadata, PalmDOC (LZ77) and HUFF/CDIC compression, MOBI 6fileposlinks andrecindexpictures, KF8 text rebuilt from the skeleton and fragment indexes withkindle:pos/kindle:embed/kindle:flowreferences resolved, and plain PalmDOC (TEXtREAd) books. AZW4 (Print Replica) gives back its PDF. - FB2: nested sections become headings, poems / epigraphs / citations / tables are kept, note links become footnotes, pictures come from the base64 binaries; the XML declaration's encoding (often windows-1251) is used. FB2 output nests sections by heading level.
- HTMLZ / TXTZ (calibre's zipped formats) are read and written, with their
metadata.opf. - KEPUB (Kobo,
Ebooks/Kepub.cs, written from the format's public descriptions): EPUB → KEPUB copies the book and changes only the XHTML documents of the spine. Every sentence becomes<span class="koboSpan" id="kobo.P.S">(P counts the block elements of the chapter, S the sentences in it; a sentence ends at . ! ? … followed by a space, or at the full-width 。!?), every picture gets a span of its own, and the body's content is wrapped in<div id="book-columns"><div id="book-inner">. Scripts, styles, SVG and MathML are left alone; a chapter that isn't well-formed XML (after HTML entities such as became character references) is copied unchanged. Files are namedname.kepub.epub, which tells Kobo devices to use their KEPUB reader. Other formats reach KEPUB through EPUB (Markdown → EPUB → KEPUB); KEPUB → EPUB removes the markup again, and a KEPUB is read like any EPUB. - Comics: CBZ, CBR, CB7, CBT and CBC (a ZIP of CBZ files) are opened with SharpCompress; pages are sorted naturally ("page2" before "page10"). → PDF has one page per picture sized like the picture (JPEGs are embedded as they are), → EPUB is fixed-layout, → CBZ repacks. PDF → CBZ renders the pages with PDFium at the preset DPI.
- Books with DRM (Adobe, Apple, Kindle) are refused with a clear message; Filee does not remove DRM. KFX and Topaz books are recognised and refused too.
calibre's ebook-convert (GPL-3.0) is an optional download for the formats Filee does not read or write itself:
LIT, LRF, CHM, PDB, PML, RB, SNB, TCR and OEB → EPUB, and EPUB → MOBI, AZW3, LIT, LRF, PDB, PML, RB, SNB and TCR.
EPUB is the hub, so for example FB2 → AZW3 is FB2 → EPUB (built in) → AZW3 (calibre). Its edges cost 20, so
built-in routes always win where they exist.
calibre also makes PDFs of EPUB, MOBI, AZW3, AZW, PRC, FB2, HTMLZ, TXTZ and the formats only it reads (one
ebook-convert run: A4 with --paper-size a4, page numbers with --pdf-page-numbers, calibre's margins and font
sizes; the built-in route ignores the preset's PDF options too). For long books it is much faster than the built-in
route, HWPX writer → rhwp (a 640-page novel: about a minute against some 48 minutes, #127), but the built-in route
stays the default so results don't change under users. The PDF edges cost 23: in the default engine order the
built-in route costs 31 (HWPX writer 10 + 8, rhwp 10 + 3) and calibre 23 + 9, so the built-in route wins by one; once
the user moves calibre above the HWPX writer in Settings → Engines, calibre costs at most 31 and the built-in route
at least 32. LIT, LRF and the other formats only calibre reads go straight to PDF instead of calibre → EPUB → HWPX
writer → rhwp. When rhwp runs out of time and calibre is installed, the error says to rank calibre higher; Filee
never switches engines by itself.
- The pinned
calibre-64bit-<version>.msifrom download.calibre-ebook.com is unpacked like LibreOffice; the folder withebook-convert.exebecomesengines/calibre. - Each run gets its own configuration, cache and temp folders in the job's work directory
(
CALIBRE_CONFIG_DIRECTORY, ...), so a calibre the user installed is never read or changed; messages are English (CALIBRE_OVERRIDE_LANG) and progress comes from its "34% ..." lines.
rhwp converts between the two 한글 formats without Hancom Office (export-hwpx and convert). Anything → HWP goes
through the HWPX writer and then rhwp.
For HWP / HWPX → PDF, rhwp renders a copy of the HWPX (an HWP is exported with export-hwpx first) when Filee has to
change something for it (Hwp/RhwpInput.cs): fonts that are installed, the laid-out size of boxes sized relative to
the column, spaced equation commands, and, in an HWPX exported from an HWP (rhwp's META-INF/rhwp-hwp5-origin
marker), the stored line of a cell paragraph that holds only a floating table moved up to the paragraph's top, so the
table is not drawn over the paragraphs below it (#49).
rhwp renders text slowly: about 1 s per KB of section XML, some 4 s per page of a novel (rhwp 0.8.7 on an
i7-1185G7, pictures cost little). So each rhwp run's time limit follows the document's text: 2 minutes + 30 minutes
per MB of unpacked Contents/section*.xml (an HWP counts three times its file size, its text being deflated), at most
60 minutes, after which a rhwp that hangs is stopped. The progress estimate assumes 15 minutes per MB. Cancelling
stops rhwp at once. A time-out fails with "rhwp couldn't finish this long document in time" (with calibre installed,
it suggests ranking calibre above the HWPX writer for e-books). The other engines keep fixed limits: LibreOffice 3
minutes, Ghostscript 5, Pandoc 2 and calibre 10 (it made the 640-page novel's PDF in a minute).
The archive engine (Archives/ArchiveConverter.cs, id archive) uses Filee's own copy of the 7-Zip console program
(7z.exe + 7z.dll, taken from the official x64 MSI with msiexec /a by build/fetch-engines.ps1 -Only 7zip and
bundled with the installer), never a 7-Zip installed on the PC. It runs as a separate process (LGPL).
| Format | Read | Write |
|---|---|---|
| ZIP, JAR | 7-Zip | ZIP in-process (System.IO.Compression): UTF-8 names with the language-encoding flag, times, empty folders |
| 7Z | 7-Zip | 7-Zip |
| TAR | 7-Zip | in-process (System.Formats.Tar, PAX: UTF-8 names, times) |
| TAR.GZ | 7-Zip, in two stages | in-process (PAX TAR + GZipStream) |
| TAR.BZ2, TAR.XZ | 7-Zip, in two stages | TAR in-process, compressed by 7-Zip |
| TAR.7Z, TAR.Z | 7-Zip, in two stages | — |
| LZ, TAR.LZ (lzip) | Filee: each member rewritten as a .lzma file for 7-Zip's LZMA decoder, CRC and size checked | — |
| GZ, BZ2, XZ | 7-Zip | 7-Zip; one file only (from another single-file format, or "Compress into one archive" with one file) |
| LZMA, Z | 7-Zip | — |
| RAR 4/5, CAB, CPIO, DEB, RPM, DMG, ISO, IMG, LHA/LZH, ARJ | 7-Zip (an RPM's payload is unpacked until the files appear) | — |
| ALZ (ALZip) | Filee: store, deflate and ALZip's bzip2 variant | — |
| EGG (ALZip) | Filee: store, deflate, bzip2, LZMA (decoded by 7-Zip); several blocks per file | — |
- Extract (target
folder): the archive is unpacked into a hidden folder next to the result and moved into place when complete. When the archive holds exactly one top folder, its content becomes the result folder (photos.zipwithphotos/…givesphotos/…, notphotos/photos/…). Name conflicts follow the preset:name (2), overwrite (files are merged into the existing folder) or skip. - Archive → archive unpacks into the job's temp folder and packs everything into the target.
Preset.Archive.Levelmaps to Store / Fastest / Optimal / SmallestSize in-process and to 7-Zip's-mx0/1/5/9. - Compress into one archive (
Archive.CombineIntoOne, the "One ZIP" preset):JobQueuehands all dropped files, of any type and unconverted, toIFileCombiner(implemented by this engine). The archive is named after the first file with the output pattern; files with the same name becomename (2).ext. ZIP, TAR and TAR.GZ need no 7-Zip. - Safety: every archive is listed first. Encrypted entries or archives stop with "password-protected"; entries
(or hard links) with
.., a drive or a leading slash stop the job ("points outside the target folder", nothing is written); symbolic links are never unpacked and any link left afterwards is deleted; archives with more than 1,000,000 entries or more data than the drive has free are refused; Filee's own readers also stop when data unpacks to more than the entry declares, and check CRCs. - Not supported: encrypted archives, split (multi-volume) ALZ and EGG, solid EGG, EGG entries compressed with ESTsoft's own AZO method, LZO / TAR.LZO (lzop; 7-Zip has no reader either), and writing RAR, ALZ or EGG.
- The ALZ and EGG readers are Filee's own code, written from the format descriptions of unalz (zlib licence), the
unalzRust crate, EggDotNet and unegg (MIT). ALZip's bzip2 has noBZhheader, starts every block withDLZ\x01instead of the block magic, CRC and randomised bit, and ends withDLZ\x02(Archives/Bzip2Decoder.cs). - Tests (
ArchiveTests.cs) build ALZ, EGG and lzip files in code (ArchiveBuilders.cs, with bzip2 and LZMA streams written by 7-Zip) and skip the 7-Zip parts whenengines/7zipis missing.
- Each parallel worker gets its own profile under
%APPDATA%\Filee\libreoffice-profiles(LibreOffice silently fails when two conversions share a profile). - HWP/HWPX import comes from the H2Orestart extension, which is written in Java; Filee points LibreOffice at the
downloaded JRE (
-env:UNO_JAVA_JFW_JREHOME). - It is the only reader of rarer formats, converted to PDF and to the editable formats of their kind: DOT, Works /
WPS Writer, WordPerfect, Lotus Word Pro, AbiWord, Apple Pages, StarWriter, ClarisWorks / AppleWorks (
.cwk), MacWrite (.mcw), T602 (.602) and Pocket Word (.psw) (→ DOCX, ODT, FODT, OTT, DOC, RTF, TXT, HTML); WPS Spreadsheets (ET), Apple Numbers, StarCalc and flat ODS (.fods) (→ XLSX, ODS, OTS, XLS, XLT, CSV, HTML); Apple Keynote, WPS Presentation (DPS), StarImpress / StarDraw and flat ODP (.fodp) (→ PPTX, ODP, OTP, PPT); Publisher, CorelDRAW, Visio, CGM, ODG and flat ODG (.fodg) (→ ODG, SVG, PNG, EMF, WMF). The--convert-tofilter follows the application that opens the file (checked against LibreOffice's filter registry): Publisher, CorelDRAW and Visio open in Draw, CGM in Impress, and "SDA" files in Draw (.sda) or Impress (.sdd). ClarisWorks files can also be spreadsheets or drawings; Filee treats them as text documents, the common case. SVG, PNG, EMF and WMF export the first page. - Written only by LibreOffice: FODT (
OpenDocument Text Flat XML), the OpenDocument templates OTT / OTS / OTP (writer8_template,calc8_template,impress8_template) and XLT (MS Excel 97 Vorlage/Template). XLT is read by the built-in spreadsheet engine like XLS. - SVG opens in Draw only for EMF, WMF and ODG; the vector engine keeps SVG → PDF and images. PDF opens in Draw
(
draw_pdf_import) only for SVG and ODG: Draw's SVG export draws every page of a PDF on top of each other, so Filee first copies the first page of the preset's page range into a PDF of its own (PDFsharp). EPS and PS reach SVG through Ghostscript's PDF; Draw would only show their preview picture. - Checked with LibreOffice 26.2.6 (
LibreOfficeFormatTests): FODT, FODS, FODP, FODG, T602 and SVG inputs, the template and FODT outputs, XLT read back by the spreadsheet engine, EMF / WMF headers and the one-page PDF → SVG. ClarisWorks, MacWrite and Pocket Word are binary formats without sample files in the repository: only their filters (ClarisWorks,MacWrite,PocketWord File, all opening in Writer) were checked in the registry.
Office/PandocConverter.cs runs Pandoc for the markup languages Filee doesn't read itself: reStructuredText,
LaTeX, Org (.org), AsciiDoc (.adoc, .asciidoc), Typst (.typ), MediaWiki (.mediawiki, .wiki),
DocBook (.dbk, .docbook; written as DocBook 5), Jupyter notebooks (.ipynb), Textile (.textile), man pages
(.man) and OPML (.opml). Pandoc 3.12 reads and writes all of them.
- Each converts to and from Markdown, HTML, DOCX and ODT and into each other, and to RTF, EPUB and TXT. Through Pandoc's JSON AST they are also read by the HWPX writer, so they reach HWPX, HWP and PDF (HWPX writer → rhwp). There is no PDF through Pandoc itself (that would need a TeX or Typst engine).
- Edges into a markup language cost 11, one more than into Markdown, so a route with a Pandoc step in the middle (an e-mail's HTML → ? → RTF) stays on Markdown.
- Markdown, reStructuredText, Org, AsciiDoc, Typst, MediaWiki, Textile and notebooks → PPTX with Pandoc's PowerPoint writer (a slide per heading). These edges cost 20: with LibreOffice's cheap PPTX → PDF, Markdown → PPTX → PDF would otherwise beat Markdown → HWPX → PDF.
- Extensions that mean other things are not claimed: man pages only as
.man(.1….9are also rotated logs,app.log.1), DocBook as.dbk/.docbook, never every.xml. - DocBook, man pages, OPML, Typst, HTML and LaTeX are written with
--standalone(a root element, a.THheader, a preamble), the text formats extract pictures into<name>_filesnext to the result.
Subtitles/SubtitleConverter.cs (engine id subtitle) converts text subtitles in managed code, no FFmpeg needed.
Every reader produces one cue model (SubtitleModel.cs: start, end and text with line breaks and SRT's own tags
<i>, <b>, <u>, <font color>), every writer takes it, so any format converts to any other and to itself (an
SRT or SAMI file in CP949 comes out as UTF-8).
| Format | Read | Written |
|---|---|---|
| SRT | numbers optional, blank lines inside cues and missing between them, , or . before the milliseconds, {\an8} codes dropped |
UTF-8 with BOM, CRLF, tags kept |
| ASS / SSA | Dialogue: lines in the order of the Format: line; \i \b \u \c, bold/italic/underline styles, \N; drawings (\p1) and comments left out, shadow layers merged |
ASS v4.00+ for 1920 × 1080, one "Default" style, override codes (SSA is read only) |
| WebVTT | NOTE / STYLE / REGION blocks skipped, cue settings ignored, <i> <b> <u>, the colour classes (<c.yellow>), entities; voices and timestamps dropped |
UTF-8, the basic colour classes, text escaped |
| SAMI (SMI) | hand-made HTML: unquoted attributes, unclosed <SYNC> / <P>,   with or without ;; of several languages (KRCC, ENCC, …) the one with the most lines |
one class from the text's language (KRCC for Korean), where the screen goes blank, UTF-8 with BOM |
| LRC | [mm:ss.xx] (several per line), [ti:], [offset:], enhanced word times dropped; a line lasts until the next one |
plain text, lines joined, an empty time line where the screen goes blank |
| TTML / DFXP | TTML 1 and the 2006 DFXP namespace; times relative to their parents; clock, frames (ttp:frameRate), ticks and offset times; spans and referenced styles |
TTML 1 with a bottom region and tts: spans |
| MicroDVD (.sub) | {frame}{frame}text with the rate of a {1}{1}25 first line (else 23.976), | between lines, {y:i} / {c:$BBGGRR}; VobSub .sub (pictures) and SubViewer fail with a clear message |
23.976 fps, first line states it, plain text, {Y:i} for all-italic cues |
| SCC | CEA-608 caption channel 1: pop-on, paint-on and roll-up captions, row preambles, italics, special and extended characters, doubled control codes, drop-frame time codes | — |
- Encodings: a byte order mark (UTF-8, UTF-16) or valid UTF-8 decide first, then a
charset=/encoding=the file declares. Otherwise CP949 when the bytes are CP949 pairs, most of them Hangul syllables of KS X 1001 and hardly any with a lead byte above C8 (Chinese GBK text uses those for many common characters), then the PC's code page on Chinese or Japanese Windows, else the Western one (Windows-1252 on Korean Windows). Everything is written as UTF-8. - Cues without an end (LRC, the last SAMI line, an unterminated SCC caption) last 4 seconds; empty cues are dropped.
- Not kept: positions and alignment, fonts and sizes, karaoke timing, ASS animations, WebVTT regions and voices.
- Tests (
SubtitleTests.cs) write every input in code, CP949 and GBK bytes included.
Not supported: DjVuLibre (GPL) has no pinned, redistributable Windows build in an archive the installer can unpack.
The official Windows release is an NSIS installer (DjVuLibre-3.5.29_DjView-4.12_Setup.exe on SourceForge), which
would have to be run (a system install) or unpacked with 7-Zip; MSYS2's packages are .pkg.tar.zst archives with
five dependency packages. A DjVu engine needs a zip of ddjvu.exe / djvutxt.exe and their DLLs published for
Filee (with the GPL source offer), then an engines.json component and an optional package.
Media/FfmpegConverter.cs converts every video format to every video format, to animated GIF or APNG, to a still PNG
frame or a folder of every frame, to every audio format (the sound track) and to SRT, ASS and WebVTT (the subtitles);
every audio format to every audio format; and GIF, APNG and PNG to MP4, WEBM and MOV.
Same-format edges exist too, so "MP4 720p" works on MP4 files and "MP3 128k" on MP3 files.
-
The download is the gyan.dev 9.0.2 "full_build-shared" Windows build (GPL-3.0, run as a separate program). It is smaller than the "essentials" build (100 vs 115 MB download, 272 vs 329 MB on disk, because ffmpeg.exe and ffprobe.exe share their libraries) and has a superset of its encoders. It links only Windows system DLLs; GPU and Vulkan libraries are loaded only if present. Being over 60 MB, it is offered but not ticked on first run.
-
Container and codecs per target (
Media/MediaEncoding.cs, every encoder checked withffmpeg -encoders):Target Written as MP4, M4V, MOV, MKV, FLV, TS, M2TS, 3GP, 3G2 H.264 (x264, preset medium, yuv420p) + AAC; MP4/M4V/MOV/3GP/3G2 with the index at the front ( +faststart)WEBM VP9 (constant quality, speed 4, row multithreading) + Opus AVI MPEG-4 part 2 tagged XVID + CBR MP3 WMV WMV 8 (wmv2) + WMA 2 MPG MPEG-2 program stream: MPEG-2 video + MP2 VOB DVD-Video: 720×480 at 29.97 fps (NTSC) or 720×576 at 25 fps (PAL, for 25/50 fps sources), letterboxed into 4:3 or 16:9, AC-3 at 48 kHz, DVD mux rate OGV Theora + Vorbis DV DV25 with the same NTSC/PAL frames as VOB, 48 kHz stereo PCM MXF OP1a with long-GOP MPEG-2 + 48 kHz PCM GIF two passes: palettegen over the whole clip, then paletteuse; 15/12/10 fps and at most 640/480/320 px wide (High/Balanced/Small) APNG one pass with the apngencoder (24-bit RGB, mixed prediction) and muxer, looping; the same frame rates and widths as GIFMP4, M4V, MKV with Video codec HEVC · MOV with HEVC x265 (preset medium, hvc1tag in MP4/M4V/MOV) + AACMP4, M4V, MKV, WEBM with Video codec AV1 SVT-AV1 (preset 8), or libaom-av1 (cpu-used 6, row multithreading) with an FFmpeg that lacks SVT-AV1 MP3 · M4A/M4B · AAC · OGG · OPUS · WEBA · MKA · WMA · MP2 · AC3 · E-AC3 · AMR LAME VBR (V0/V2/V5) or CBR · AAC, or ALAC with Audio codec ALAC · AAC (ADTS) · Vorbis · Opus · Opus · Opus · WMA 2 · MP2 · AC-3 · E-AC-3 · AMR-NB 12.2k (8 kHz mono) WAV · W64 · AIFF · AU · CAF · VOC · FLAC · WV · TTA 16-bit PCM (WAV and W64: 8/16/24/32-bit or 32-bit float; WAV switches to RF64 over 4 GB, Sony Wave64 has no limit) · FLAC · WavPack (lossless) · TTA SRT · ASS · VTT (from video) the video's first text subtitle track, copied when it already is that format, else converted by FFmpeg Read only (FFmpeg's decoders): APE (Monkey's Audio), Musepack (MPC), TAK, DSF and DFF (DSD), DTS, Speex (SPX) and the formats above. No encoder for them is in the build, or (DTS) only an experimental one.
-
Rewrapping instead of re-encoding. When the target container takes the source's codecs as they are (
MediaEncoding.Remuxable: H.264/HEVC + AAC, ALAC, AC-3, E-AC-3 or MP3 for MP4/M4V/MOV, also ProRes and PCM for MOV; H.264/HEVC/VP9/AV1 with most audio for MKV; VP8/VP9/AV1 + Opus/Vorbis for WebM; H.264/HEVC for TS/M2TS), the streams are copied (-c copy). An iPhone MOV becomes an MP4 in seconds, frame for frame, with 10-bit HDR (HLG, Dolby Vision's HLG base), rotation and colour tags kept. HEVC gets thehvc1tag Apple players need. Video is copied only in a pixel format players decode (4:2:0, 8 or 10 bits; ProRes as it is) and when no scaling down is needed; audio only when no bitrate or other channel count is set. Quality Small always re-encodes (the user asked for a smaller file); High and Balanced only set how a stream that has to be encoded is encoded. A stream that can't be copied is encoded as below, the other one is still copied. -
Preset options (
MediaOptions): Quality picks CRF 18/23/28 (x264), 22/26/30 (x265), 26/32/40 (SVT-AV1 and libaom), 24/32/38 (VP9), quantizer 2/4/7 (MPEG-4, WMV, MPEG-2), Theora 8/6/4 and the audio bitrates (e.g. AAC and E-AC-3 256/192/128 kbit/s per stereo pair); MaxHeight scales down only, keeps the aspect ratio and even sizes; AudioBitrateKbps overrides the audio bitrate (snapped to the MP3/MP2/AC-3 bitrate tables; the editor offers 32 to 320 kbit/s); RemoveAudio drops the sound; AudioChannels (0 source, 1 mono, 2 stereo) applies to every audio target but AMR; AudioSampleRate (8 to 48 kHz) and WavFormat (bit depth) apply to WAV and W64. -
VideoCodec (Default, HEVC, AV1) and AudioCodec (Default, ALAC) pick another codec where the target takes it (
MediaCodecsin Filee.Core: HEVC for MP4/M4V/MKV/MOV, AV1 for MP4/M4V/MKV/WEBM, ALAC for M4A/M4B); for any other target the usual codec is written. The presets "MP4 HEVC" (on the video donut), "MP4 AV1" and "ALAC" use them. A video already in the chosen codec is copied (the rules above, with AV1 also into MP4); HEVC and AV1 keep 10 bits (yuv420p10le, Main 10) for 10-bit and HDR sources and write their colour tags (BT.2020, PQ or HLG), 8-bit 4:2:0 for the rest. The encoders are looked up withffmpeg -encodersonce per FFmpeg, so a copy the user picked without x265 or AV1 fails with a clear message instead of an FFmpeg error. x265 and SVT-AV1 log only errors (-x265-params log-level=error,SVT_LOG=1). -
Hardware encoding (Hardware acceleration in the video card,
MediaOptions.HardwareEncoding; off by default and for older presets, so nobody's results change without asking). H.264 and HEVC encodes (not VP9, AV1 and the MPEG-style codecs, not stream copies, not pictures → video) go to the graphics chip (Media/HardwareVideo.cs,Media/HardwareProbe.cs):System Tried in this order Windows *_nvenc(NVIDIA),*_qsv(Intel Quick Sync),*_amf(AMD)macOS *_videotoolboxwith constant quality (Apple Silicon), then with a bitrate (Intel Macs reject-q:v)Linux *_nvenc, then*_vaapion/dev/dri/renderD128(-vaapi_device, frames uploaded withformat=nv12,hwupload)FFmpeg lists encoders the computer can't run (NVENC without an NVIDIA card), so only the ones
ffmpeg -encoderslists are tried, each first with a test encode with the real options (0.1 s oftestsrcat 256 × 144 to the null muxer, 15 s limit). The first that succeeds is used; results are kept per FFmpeg executable, encoder, rate control and bit depth until Filee restarts, and written to the log. Quality: High/Balanced/Small become quantizer 19/24/29 for H.264 and 23/27/31 for HEVC (one above x264's and x265's CRF: hardware encoders spend bits less wisely) as NVENC-rc vbr -cq N -b:v 0 -preset p5, Quick Sync-global_quality N -preset medium(ICQ), AMF-rc cqp -qp_p N -qp_i N−2(-quality balanced) and VA-API-rc_mode CQP -qp N; VideoToolbox gets-q:v70/60/50, or on Intel Macs-b:vfrom 0.14/0.09/0.05 bits per pixel per frame (× 0.65 for HEVC). 8-bit video is NV12 (H.264 High, HEVC Main); 10-bit and HDR sources stay P010 HEVC Main 10 with their colour tags when the encoder passes a 10-bit test encode, otherwise x265 encodes them. HEVC keeps thehvc1tag in MP4/M4V/MOV. When a hardware encode fails anyway (a picture too large for the chip, a driver error), the partial output is deleted and the file is encoded again with x264/x265; that is logged, not shown as a failure. -
DSD (DSF, DFF) decodes at 2.8 MHz or more: lossless and PCM targets get 88.2 kHz, Vorbis and WMA 44.1 kHz, the other lossy encoders their nearest supported rate.
-
Subtitle extraction takes the first text subtitle track (SubRip, ASS/SSA, WebVTT, MP4 timed text, CEA-608, …). A video without subtitles fails with "The video has no subtitle track."; one with only picture subtitles (Blu-ray PGS, DVD, DVB) says they can't be turned into text without OCR. Video → SAMI, LRC, TTML, … goes through SRT and the subtitle engine (ASS and WebVTT extraction cost 11 so SRT is the hub).
-
The first video stream (cover pictures excluded) and the first audio stream are converted; files without sound still convert to video. Title, artist and other tags and chapters are kept, album art too for MP3, M4A, M4B and FLAC. Portrait phone videos are turned upright. Odd sizes are evened out, frame rates that MPEG-2/MXF/MPEG-4 part 2 cannot store are converted, and channels WMA or AMR cannot hold are mixed down.
-
Progress comes from
-progress(position ÷ duration from ffprobe); the GIF palette pass reports per-frame stats of a discarded copy of its frames. Cancelling kills the process tree, waits (up to 10 seconds) until it has exited and deletes the partial output. There is no time limit, but FFmpeg is stopped when it reports nothing for 10 minutes. At most two conversions run at a time (FFmpeg's encoders use every core themselves). -
Video → PNG writes one still frame (a tenth into the video, where the picture has usually faded in); the image engines turn it into JPG, WEBP, PDF, …. The GIF edge costs more, so these routes never go through an animated GIF of the whole video.
-
Every frame (Save every frame of a video on a PNG or JPG preset,
MediaOptions.SaveEveryFrame; the presets "Extract frames (PNG)" and "Extract frames (JPG)", on no donut):Media/FrameSequenceWriter.cswritesframe_000001.png,frame_000002.png, … into one folder, named by the output rule with_framesadded (clip_frames,clip_frames (2); skipped, or filled over like an extracted archive, by the conflict rule).- Routes: the frame edges (
ConversionEdge.FrameSequence, video → PNG and video → JPG) are taken only whenPreset.SavesFramesOfthe file (the option is set, the target is PNG or JPG and the file is a video), and then as the whole route (RoutePlanner.Plan(from, to, frames: true)). Without the option video → JPG stays the still frame above, which the image engines turn, resize and so on; other files and targets ignore the option. - Every Nth frame (1–1000) keeps frames 1, N + 1, 2N + 1, … (
select,-fps_mode vfr). Otherwise every decoded frame is written once (-fps_mode passthrough): a variable frame rate phone video gives as many pictures as it has frames, not one per 1/30 s with the slow parts repeated. The pictures are numbered, not named by time. - Only the first video stream is read (
-map 0:V:0). Pixels are made square and portrait videos are turned upright, as for the still frame; nothing else of the image options is applied (no resizing, rotating or metadata). JPG uses the preset's quality (100 →-q:v 2, 90 → 5, 1 → 31). HDR video (PQ, HLG) is not tone-mapped, so its frames look flat and grey; 10-bit video becomes 16-bit PNG. - Before anything is written the pictures are estimated (duration × frame rate ÷ interval). More than 100,000
(
MediaOptions.MaxFrames, about an hour at 30 fps), or more than the free space of the output drive (estimated at 1.5 bytes a pixel for PNG, 3 for 10-bit video, 0.25 for JPG), fails with a reason in the user's language (ConversionRefusedException); a file that doesn't say its length or frame rate is not checked. The editor shows how many pictures a minute of 30 fps video gives at the chosen interval. - The frames go into a hidden folder next to the output and are moved into place when FFmpeg has finished, so a cancelled or failed run leaves no half-filled folder.
- Routes: the frame edges (
-
Pictures → MP4/WEBM/MOV go through a full-size PNG from the image engines (HEIC → PNG → MOV): a 5-second still at 30 fps, one quality step above the preset (identical frames cost almost nothing; x264
-tune stillimage). The PNG's ICC profile is read (-flags2 +icc_profiles), so primaries and transfer are tagged as the photo's (Display P3 stays P3); RGB → BT.709 YUV in TV range; transparency is premultiplied onto black. The size is kept (made even) unless it exceeds H.264/VP9 level 6.2 (8192 × 4352 pixels, 16384 per side) or the preset's maximum height. The image engines' still picture → GIF and → APNG edges cost 12 (ImageEncoder.Edge) and PNG → video 11, so a photo takes the PNG route (21 < 22) while animated GIF/WebP/MNG, multi-page TIFF and PDF keep the GIF route (20): a moving video or a slideshow of the pages. APNG → video costs 11, so an APNG goes straight to video and nothing else detours through it. -
Images → HEIC (
Heic/HeicWriter.cs): ImageMagick has no HEVC encoder and FFmpeg no HEIF muxer, so the work is split. ImageMagick reads any image it knows and applies the preset (orientation, resize, metadata; transparency is flattened onto the background colour, HEIC alpha needs an auxiliary picture Filee does not write) and writes an 8-bit PNG. FFmpeg pads it to whole 512 × 512 tiles with its own edge pixels (fillborders), converts to BT.601 full-range 4:2:0 like JPEG and cameras, cuts the tiles (untile) and has x265 encode each as an IDR picture of the Main profile (-force_key_frames expr:1 -forced-idr 1,open-gop=0; withkeyint=1x265 would signal the "Main Intra" range extensions profile, which theheicbrand does not allow and Windows decodes wrongly). Pictures up to 512 × 512 are one coded picture. Quality 1–100 maps to CRF 50 − 0.4 × quality (90 → 14, 80 → 18).HevcStreamsplits the Annex B output and builds the hvcC record;HeifFilewrites ftypheic/mif1, a grid item cropping the tiles to the picture size, hidden tile items,ispe/pixi, the ICC profile ascolr prof(ornclxsRGB, BT.601 full range when there is none) and Exif (orientation reset to 1, thumbnail removed) as acdscitem. Checked with libheif (Magick.NET), FFmpeg's HEIF demuxer and Windows' HEIF/HEVC extensions; a smooth 2000 × 1500 picture is a quarter the size of a JPG at the same PSNR.