diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml
index 26dd30a..0dcab6a 100644
--- a/.github/workflows/c-cpp.yml
+++ b/.github/workflows/c-cpp.yml
@@ -8,18 +8,38 @@ on:
jobs:
build:
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest]
+ include:
+ - os: ubuntu-latest
+ install_deps: sudo apt-get update && sudo apt-get install -y autoconf automake libtool libiconv-hook-dev || true
+ - os: macos-latest
+ install_deps: brew install autoconf automake libtool libiconv || true
- runs-on: ubuntu-latest
+ runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: ${{ matrix.install_deps }}
+
- name: autoreconf
run: autoreconf -vfi
+
- name: configure
run: ./configure
+
- name: make
run: make
+
- name: make check
run: make check
-# - name: make distcheck
-# run: make distcheck
+
+ - name: Run integration tests
+ run: |
+ if [ -f tests/run_tests.sh ]; then
+ chmod +x tests/run_tests.sh
+ ./tests/run_tests.sh
+ fi
diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml
index e282e38..7c656b2 100644
--- a/.github/workflows/cmake.yml
+++ b/.github/workflows/cmake.yml
@@ -16,10 +16,22 @@ jobs:
# well on Windows or Mac. You can convert this to a matrix build if you need
# cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
- runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest]
+ include:
+ - os: ubuntu-latest
+ install_deps: sudo apt-get update && sudo apt-get install -y libiconv-hook-dev || true
+ - os: macos-latest
+ install_deps: brew install libiconv || true
+
+ runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: ${{ matrix.install_deps }}
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
@@ -32,7 +44,7 @@ jobs:
- name: Test
working-directory: ${{github.workspace}}/build
- # Execute tests defined by the CMake configuration.
+ # Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
- run: ctest -C ${{env.BUILD_TYPE}}
-
+ run: ctest -C ${{env.BUILD_TYPE}} --output-on-failure
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4891825..b6c11d0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,18 +1,34 @@
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
-project(libdxfrw)
+project(libdxfrw VERSION 0.6.3)
+
+# Enable testing
+enable_testing()
file(GLOB libdxfrw_sources src/*.cpp)
file(GLOB libdxfrw_headers include/*.h)
file(GLOB libdxfrw_intern_sources src/intern/*.cpp)
+# Find iconv library
if(WIN32)
-
-include_directories(vs2013/packages/libiconv.1.14.0.11/build/native/include)
-link_directories(vs2013/packages/libiconv.1.14.0.11/build/native/lib)
+ include_directories(vs2013/packages/libiconv.1.14.0.11/build/native/include)
+ link_directories(vs2013/packages/libiconv.1.14.0.11/build/native/lib)
+ set(ICONV_LIBRARY iconv)
+else()
+ # On Unix-like systems (Linux, macOS)
+ find_package(Iconv REQUIRED)
+ if(Iconv_FOUND)
+ include_directories(${Iconv_INCLUDE_DIRS})
+ set(ICONV_LIBRARY ${Iconv_LIBRARIES})
+ else()
+ # Fallback: try to link against iconv directly
+ set(ICONV_LIBRARY iconv)
+ endif()
endif()
+
include_directories(include)
add_library(dxfrw STATIC ${libdxfrw_sources} ${libdxfrw_intern_sources})
+target_link_libraries(dxfrw ${ICONV_LIBRARY})
install(FILES ${libdxfrw_headers} DESTINATION include)
@@ -29,4 +45,45 @@ else()
install(TARGETS dxfrw
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
-endif()
\ No newline at end of file
+endif()
+
+# Tests
+add_executable(test_basic tests/test_basic.cpp)
+target_include_directories(test_basic PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
+target_link_libraries(test_basic dxfrw ${ICONV_LIBRARY})
+add_test(NAME BasicTests COMMAND test_basic)
+
+add_executable(test_entities tests/test_entities.cpp)
+target_include_directories(test_entities PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
+target_link_libraries(test_entities dxfrw ${ICONV_LIBRARY})
+add_test(NAME EntityTests COMMAND test_entities)
+
+add_executable(test_polylines tests/test_polylines.cpp)
+target_include_directories(test_polylines PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
+target_link_libraries(test_polylines dxfrw ${ICONV_LIBRARY})
+add_test(NAME PolylineTests COMMAND test_polylines)
+
+add_executable(test_text tests/test_text.cpp)
+target_include_directories(test_text PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
+target_link_libraries(test_text dxfrw ${ICONV_LIBRARY})
+add_test(NAME TextTests COMMAND test_text)
+
+add_executable(test_tables tests/test_tables.cpp)
+target_include_directories(test_tables PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
+target_link_libraries(test_tables dxfrw ${ICONV_LIBRARY})
+add_test(NAME TableTests COMMAND test_tables)
+
+add_executable(test_blocks tests/test_blocks.cpp)
+target_include_directories(test_blocks PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
+target_link_libraries(test_blocks dxfrw ${ICONV_LIBRARY})
+add_test(NAME BlockTests COMMAND test_blocks)
+
+add_executable(test_versions tests/test_versions.cpp)
+target_include_directories(test_versions PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
+target_link_libraries(test_versions dxfrw ${ICONV_LIBRARY})
+add_test(NAME VersionTests COMMAND test_versions)
+
+add_executable(test_errors tests/test_errors.cpp)
+target_include_directories(test_errors PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
+target_link_libraries(test_errors dxfrw ${ICONV_LIBRARY})
+add_test(NAME ErrorTests COMMAND test_errors)
\ No newline at end of file
diff --git a/Makefile.am b/Makefile.am
index 32ee84e..4e9abe0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,3 @@
ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = src dwg2dxf dwg2text
+SUBDIRS = src dwg2dxf dwg2text tests
diff --git a/SPECIFICATIONS.md b/SPECIFICATIONS.md
new file mode 100644
index 0000000..706f8e1
--- /dev/null
+++ b/SPECIFICATIONS.md
@@ -0,0 +1,548 @@
+# libdxfrw - Technical Specifications
+
+## Project Overview
+
+**Project Name**: libdxfrw
+**Version**: 0.6.3
+**License**: GNU General Public License v2 (or later)
+**Original Author**: José F. Soriano (Rallaz)
+**Maintainers**: codelibs, Nicu Tofan, Miguel E. Hernández Cuervo
+
+### Purpose
+
+libdxfrw is a free C++ library for reading and writing DXF (Drawing eXchange Format) files in both ASCII and binary formats. It also supports reading DWG (AutoCAD drawing) files from versions R14 through V2015.
+
+### Main Features
+
+1. **DXF File Read/Write**
+ - Read and write DXF files in ASCII format
+ - Read and write DXF files in binary format
+ - Support for multiple DXF versions: R12, 2000, 2004, 2007, 2010
+
+2. **DWG File Reading**
+ - Supported versions: R14 to V2015
+ - Version-specific reader implementations:
+ - dwgReader15: R14 version
+ - dwgReader18: R2004 version
+ - dwgReader21: R2007 version
+ - dwgReader24: R2010 version
+ - dwgReader27: R2013/R2015 versions
+
+3. **Supported CAD Drawing Entities**
+ - Basic shapes: Points, Lines, Rays, XLines
+ - Curves: Circles, Arcs, Ellipses, Splines
+ - Polylines: 2D/3D Lightweight Polylines
+ - 3D shapes: 3D Faces, Traces, Solids
+ - Text: Single-line text, Multi-line text
+ - Annotations: Hatches, Dimensions, Leaders
+ - Others: Block references, Images, Viewports
+
+4. **Supported CAD Objects**
+ - Layers
+ - Line Types
+ - Text Styles
+ - Dimension Styles
+ - Viewports
+ - Application IDs
+ - Block Definitions
+
+---
+
+## Architecture
+
+### Core Class Structure
+
+```
+dxfRW (Main API Class)
+├── DRW_Interface (Abstract Interface)
+├── dxfReader/dxfWriter (DXF I/O Handlers)
+├── dwgReader (DWG Reader Base)
+│ ├── dwgReader15 (R14)
+│ ├── dwgReader18 (R2004)
+│ ├── dwgReader21 (R2007)
+│ ├── dwgReader24 (R2010)
+│ └── dwgReader27 (R2013-2015)
+├── DRW_Header (Header Variables)
+├── DRW_Entity (Geometric Entities)
+│ ├── DRW_Point
+│ ├── DRW_Line
+│ ├── DRW_Arc
+│ ├── DRW_Circle
+│ ├── DRW_Ellipse
+│ ├── DRW_LWPolyline
+│ ├── DRW_Polyline
+│ ├── DRW_Spline
+│ ├── DRW_Text
+│ ├── DRW_MText
+│ ├── DRW_Hatch
+│ ├── DRW_Dimension (various dimension types)
+│ └── Other entities
+└── DRW_Object (Non-geometric Objects)
+ ├── DRW_Layer
+ ├── DRW_LType
+ ├── DRW_Textstyle
+ ├── DRW_Dimstyle
+ └── Other objects
+```
+
+### Directory Structure
+
+```
+libdxfrw/
+├── src/ # Library source code
+│ ├── libdxfrw.h/cpp # Main API class
+│ ├── drw_interface.h # Abstract interface
+│ ├── drw_entities.h/cpp # Entity definitions
+│ ├── drw_objects.h/cpp # Object definitions
+│ ├── drw_header.h/cpp # Header variables
+│ ├── drw_base.h # Basic data structures
+│ └── intern/ # Internal implementation
+│ ├── dxfreader.h/cpp # DXF reader
+│ ├── dxfwriter.h/cpp # DXF writer
+│ ├── dwgreader*.h/cpp # DWG readers
+│ ├── dwgbuffer.h/cpp # DWG buffer
+│ ├── drw_textcodec.h/cpp # Character encoding
+│ └── Other utilities
+├── dwg2dxf/ # DWG to DXF converter tool
+├── dwg2text/ # DWG to text extractor tool
+├── bin/ # Executable scripts
+└── vs2013/ # Visual Studio 2013 project
+```
+
+---
+
+## API Specifications
+
+### Main Class: dxfRW
+
+#### Constructor
+
+```cpp
+dxfRW(const char* name);
+```
+
+- **Parameters**: `name` - File name to process
+- **Description**: Creates an instance for processing DXF/DWG files
+
+#### Main Methods
+
+##### Reading
+
+```cpp
+bool read(DRW_Interface *interface_, bool ext);
+```
+
+- **Parameters**:
+ - `interface_`: Callback interface implementation
+ - `ext`: Apply extrusion to convert to 2D
+- **Returns**: true on success, false on failure
+- **Description**: Reads the file and calls interface methods for each entity
+
+##### Writing
+
+```cpp
+bool write(DRW_Interface *interface_, DRW::Version ver, bool bin);
+```
+
+- **Parameters**:
+ - `interface_`: Interface implementation providing data
+ - `ver`: DXF version (R12, 2000, 2004, 2007, 2010)
+ - `bin`: Output in binary format
+- **Returns**: true on success, false on failure
+
+##### Entity Writing Methods
+
+```cpp
+bool writePoint(DRW_Point *ent);
+bool writeLine(DRW_Line *ent);
+bool writeCircle(DRW_Circle *ent);
+bool writeArc(DRW_Arc *ent);
+bool writeEllipse(DRW_Ellipse *ent);
+bool writeLWPolyline(DRW_LWPolyline *ent);
+bool writeSpline(DRW_Spline *ent);
+bool writeText(DRW_Text *ent);
+bool writeMText(DRW_MText *ent);
+bool writeHatch(DRW_Hatch *ent);
+// ... many more
+```
+
+##### Table Object Writing Methods
+
+```cpp
+bool writeLayer(DRW_Layer *ent);
+bool writeLineType(DRW_LType *ent);
+bool writeTextstyle(DRW_Textstyle *ent);
+bool writeDimstyle(DRW_Dimstyle *ent);
+bool writeVport(DRW_Vport *ent);
+bool writeAppId(DRW_AppId *ent);
+```
+
+### Interface: DRW_Interface
+
+Applications must inherit from this interface and implement callback methods.
+
+#### Required Methods
+
+##### Header
+
+```cpp
+virtual void addHeader(const DRW_Header* data) = 0;
+```
+
+##### Table Objects
+
+```cpp
+virtual void addLayer(const DRW_Layer& data) = 0;
+virtual void addLType(const DRW_LType& data) = 0;
+virtual void addDimStyle(const DRW_Dimstyle& data) = 0;
+virtual void addTextStyle(const DRW_Textstyle& data) = 0;
+virtual void addVport(const DRW_Vport& data) = 0;
+virtual void addAppId(const DRW_AppId& data) = 0;
+```
+
+##### Blocks
+
+```cpp
+virtual void addBlock(const DRW_Block& data) = 0;
+virtual void setBlock(const int handle) = 0;
+virtual void endBlock() = 0;
+```
+
+##### Entities
+
+```cpp
+virtual void addPoint(const DRW_Point& data) = 0;
+virtual void addLine(const DRW_Line& data) = 0;
+virtual void addRay(const DRW_Ray& data) = 0;
+virtual void addXline(const DRW_Xline& data) = 0;
+virtual void addCircle(const DRW_Circle& data) = 0;
+virtual void addArc(const DRW_Arc& data) = 0;
+virtual void addEllipse(const DRW_Ellipse& data) = 0;
+virtual void addLWPolyline(const DRW_LWPolyline& data) = 0;
+virtual void addPolyline(const DRW_Polyline& data) = 0;
+virtual void addSpline(const DRW_Spline* data) = 0;
+virtual void addInsert(const DRW_Insert& data) = 0;
+virtual void addTrace(const DRW_Trace& data) = 0;
+virtual void add3dFace(const DRW_3Dface& data) = 0;
+virtual void addSolid(const DRW_Solid& data) = 0;
+virtual void addMText(const DRW_MText& data) = 0;
+virtual void addText(const DRW_Text& data) = 0;
+virtual void addHatch(const DRW_Hatch *data) = 0;
+virtual void addViewport(const DRW_Viewport& data) = 0;
+virtual void addImage(const DRW_Image *data) = 0;
+```
+
+##### Dimensions
+
+```cpp
+virtual void addDimAlign(const DRW_DimAligned *data) = 0;
+virtual void addDimLinear(const DRW_DimLinear *data) = 0;
+virtual void addDimRadial(const DRW_DimRadial *data) = 0;
+virtual void addDimDiametric(const DRW_DimDiametric *data) = 0;
+virtual void addDimAngular(const DRW_DimAngular *data) = 0;
+virtual void addDimAngular3P(const DRW_DimAngular3p *data) = 0;
+virtual void addDimOrdinate(const DRW_DimOrdinate *data) = 0;
+```
+
+##### Others
+
+```cpp
+virtual void addLeader(const DRW_Leader *data) = 0;
+virtual void linkImage(const DRW_ImageDef *data) = 0;
+virtual void addComment(const char* comment) = 0;
+```
+
+##### Writing Methods
+
+```cpp
+virtual void writeHeader(DRW_Header& data) = 0;
+virtual void writeBlocks() = 0;
+virtual void writeBlockRecords() = 0;
+virtual void writeEntities() = 0;
+virtual void writeLTypes() = 0;
+virtual void writeLayers() = 0;
+virtual void writeTextstyles() = 0;
+virtual void writeVports() = 0;
+virtual void writeDimstyles() = 0;
+virtual void writeAppId() = 0;
+```
+
+---
+
+## Build Systems
+
+### 1. Autotools (Recommended)
+
+```bash
+autoreconf -vfi
+./configure
+make
+sudo make install
+```
+
+#### Configuration Files
+- `configure.ac`: Autoconf configuration
+- `Makefile.am`: Automake templates
+- Library version: 0.6.3 (CURRENT=6, REVISION=3, AGE=0)
+
+#### Dependencies
+- **Required**: libiconv (character conversion)
+- **Standard Library**: stdlib.h, string.h
+- **Math Functions**: sqrt()
+
+### 2. CMake
+
+```bash
+mkdir build
+cd build
+cmake .. -DCMAKE_BUILD_TYPE=Release
+cmake --build . --config Release
+sudo cmake --build . --config Release --target install
+```
+
+#### Key CMake Settings
+- Minimum CMake version: 3.10
+- Build target: Static library `dxfrw`
+- Installation paths:
+ - Headers: `include/`
+ - Library: `lib/` (Linux/macOS), `Debug/lib/` or `Release/lib/` (Windows)
+
+### 3. Visual Studio 2013
+
+- Solution file: `vs2013/libdxfrw.sln`
+- Project file: `vs2013/libdxfrw.vcxproj`
+- NuGet package: libiconv 1.14.0.11
+
+### 4. Docker
+
+```bash
+# Build image
+docker build --rm -t codelibs/libdxfrw .
+
+# Build library
+docker run -t --rm -v `pwd`:/work codelibs/libdxfrw:latest /work/build.sh
+```
+
+- Base image: CentOS 7
+- Output: `dxfrw.tar.gz` (install location: `/opt`)
+
+---
+
+## Utility Tools
+
+### 1. dwg2dxf
+
+Command-line tool to convert DWG files to DXF format.
+
+```bash
+dwg2dxf [options] input.dwg [output.dxf]
+```
+
+**Files**:
+- `dwg2dxf/main.cpp`: Entry point
+- `dwg2dxf/dx_iface.h/cpp`: Interface implementation
+- `dwg2dxf/dwg2dxf.1`: Man page
+
+### 2. dwg2text
+
+Tool to extract text information from DWG files.
+
+```bash
+dwg2text input.dwg
+```
+
+**Files**:
+- `dwg2text/main.cpp`: Entry point
+- `dwg2text/dx_iface.h/cpp`: Interface implementation
+- `dwg2text/dwg2text.1`: Man page
+
+### 3. dxf2txt.py
+
+Python script to dump DXF files in text format.
+
+```bash
+python bin/dxf2txt.py input.dxf
+```
+
+---
+
+## Data Structures
+
+### Basic Types
+
+```cpp
+namespace DRW {
+ enum Version {
+ UNKNOWNV, // Unknown
+ R12, // R12 DXF
+ R14, // R14 DXF
+ R2000, // R2000 DXF
+ R2004, // R2004 DXF
+ R2007, // R2007 DXF
+ R2010, // R2010 DXF
+ R2013, // R2013 DXF
+ R2015 // R2015 DXF
+ };
+}
+```
+
+### Entity Base Class
+
+```cpp
+class DRW_Entity {
+public:
+ enum DRW_EntityType {
+ POINT, LINE, CIRCLE, ARC, ELLIPSE,
+ LWPOLYLINE, POLYLINE, SPLINE,
+ INSERT, TEXT, MTEXT, HATCH,
+ DIMENSION, LEADER, VIEWPORT, IMAGE,
+ // ... others
+ };
+
+ DRW_Coord basePoint; // Base point
+ std::string layer; // Layer name
+ std::string lineType; // Line type
+ int color; // Color number
+ double thickness; // Thickness
+ double ltypeScale; // Line type scale
+ int handle; // Handle
+ // ... other properties
+};
+```
+
+### Coordinate Type
+
+```cpp
+class DRW_Coord {
+public:
+ double x;
+ double y;
+ double z;
+};
+```
+
+---
+
+## Character Encoding Support
+
+libdxfrw supports multiple character code pages:
+
+- **CP932**: Japanese (Shift-JIS)
+- **CP936**: Simplified Chinese (GB2312)
+- **CP949**: Korean
+- **CP950**: Traditional Chinese (Big5)
+
+### Implementation Files
+
+- `src/intern/drw_textcodec.h/cpp`: Character encoding conversion engine
+- `src/intern/drw_cptable932.h`: Japanese code page
+- `src/intern/drw_cptable936.h`: Simplified Chinese
+- `src/intern/drw_cptable949.h`: Korean
+- `src/intern/drw_cptable950.h`: Traditional Chinese
+- `src/intern/drw_cptables.h`: Code page table integration
+
+---
+
+## Error Handling and Debugging
+
+### Debug Level Settings
+
+```cpp
+void setDebug(DRW::DBG_LEVEL lvl);
+```
+
+**Debug Levels**:
+- `DRW::NONE`: No debug output
+- `DRW::DEBUG`: Debug information output
+
+### Debug Utilities
+
+- `src/intern/drw_dbg.h/cpp`: Debug output utilities
+
+---
+
+## Performance Considerations
+
+### Buffering
+
+- `dwgBuffer`: Efficient binary data processing when reading DWG files
+- Supports bit-level read operations
+
+### Memory Management
+
+- Entities and objects are passed by pointer or reference
+- Memory management is handled by the interface implementation side
+
+### Ellipse to Polyline Conversion
+
+```cpp
+void setEllipseParts(int parts);
+```
+
+Set the number of segments when converting ellipses to polylines. Increasing the number of segments improves accuracy.
+
+---
+
+## Test Data
+
+External test data repository:
+- [fess-testdata/autocad](https://github.com/codelibs/fess-testdata/tree/master/autocad)
+
+Contains sample files of various DXF/DWG file versions, useful for integration testing.
+
+---
+
+## Known Limitations
+
+1. **DWG File Writing Not Supported**: Only DWG file reading is supported
+2. **Limited Entity Support**: May not support all entities from the latest AutoCAD versions
+3. **Reed-Solomon Decoding**: Error correction functionality for DWG files is limited
+
+---
+
+## Version History
+
+See the `ChangeLog` file for details.
+
+Major development milestones:
+- **2011**: Project started
+- **2013**: Added DWG R2004 support
+- **2014**: Added DWG R2010/R2013 support
+- **2015**: Added DWG R2015 support
+- **2020s**: Ongoing community maintenance
+
+---
+
+## License
+
+GNU General Public License v2.0 (or later)
+
+See the `COPYING` file for details.
+
+---
+
+## Contributors
+
+- **José F. Soriano (Rallaz)**: Original author
+- **Nicu Tofan**: Contributor
+- **Miguel E. Hernández Cuervo**: Contributor
+- **codelibs**: Maintainer
+
+---
+
+## References
+
+- Official website: http://sourceforge.net/projects/libdxfrw
+- GitHub: https://github.com/codelibs/libdxfrw
+- DXF Reference: AutoCAD DXF Specification (Autodesk official documentation)
+
+---
+
+## Support and Community
+
+Report issues and submit pull requests through the GitHub repository:
+https://github.com/codelibs/libdxfrw/issues
+
+---
+
+Last Updated: 2025-11-08
diff --git a/configure.ac b/configure.ac
index a751d95..1429c52 100644
--- a/configure.ac
+++ b/configure.ac
@@ -65,6 +65,7 @@ AC_CONFIG_FILES([
src/Makefile
dwg2dxf/Makefile
dwg2text/Makefile
+ tests/Makefile
libdxfrw${LIBRARY_AGE}.pc:libdxfrw.pc.in
])
AC_OUTPUT
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..f7eac61
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,36 @@
+TESTS = test_basic test_entities test_polylines test_text test_tables test_blocks test_versions test_errors
+check_PROGRAMS = test_basic test_entities test_polylines test_text test_tables test_blocks test_versions test_errors
+
+test_basic_SOURCES = test_basic.cpp test_interface.h
+test_basic_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/tests
+test_basic_LDADD = $(top_builddir)/src/libdxfrw.la
+
+test_entities_SOURCES = test_entities.cpp test_interface.h
+test_entities_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/tests
+test_entities_LDADD = $(top_builddir)/src/libdxfrw.la
+
+test_polylines_SOURCES = test_polylines.cpp test_interface.h
+test_polylines_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/tests
+test_polylines_LDADD = $(top_builddir)/src/libdxfrw.la
+
+test_text_SOURCES = test_text.cpp test_interface.h
+test_text_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/tests
+test_text_LDADD = $(top_builddir)/src/libdxfrw.la
+
+test_tables_SOURCES = test_tables.cpp test_interface.h
+test_tables_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/tests
+test_tables_LDADD = $(top_builddir)/src/libdxfrw.la
+
+test_blocks_SOURCES = test_blocks.cpp test_interface.h
+test_blocks_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/tests
+test_blocks_LDADD = $(top_builddir)/src/libdxfrw.la
+
+test_versions_SOURCES = test_versions.cpp test_interface.h
+test_versions_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/tests
+test_versions_LDADD = $(top_builddir)/src/libdxfrw.la
+
+test_errors_SOURCES = test_errors.cpp test_interface.h
+test_errors_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/tests
+test_errors_LDADD = $(top_builddir)/src/libdxfrw.la
+
+CLEANFILES = test_output.dxf test_binary.dxf test_*.dxf *.dxf
diff --git a/tests/test_basic.cpp b/tests/test_basic.cpp
new file mode 100644
index 0000000..456a895
--- /dev/null
+++ b/tests/test_basic.cpp
@@ -0,0 +1,294 @@
+/******************************************************************************
+** libDXFrw - Basic Tests **
+** **
+** Copyright (C) 2025 libdxfrw contributors **
+** **
+** This library is free software, licensed under the terms of the GNU **
+** General Public License as published by the Free Software Foundation, **
+** either version 2 of the License, or (at your option) any later version. **
+** You should have received a copy of the GNU General Public License **
+** along with this program. If not, see . **
+******************************************************************************/
+
+#include "libdxfrw.h"
+#include "test_interface.h"
+#include
+#include
+#include
+
+bool testWriteBasicDXF() {
+ std::cout << "\n=== Test: Write Basic DXF ===" << std::endl;
+
+ const char* filename = "test_output.dxf";
+ dxfRW dxf(filename);
+
+ // Create a simple writer interface
+ class WriterInterface : public DRW_Interface {
+ public:
+ virtual void addHeader(const DRW_Header* data) {}
+ virtual void addLType(const DRW_LType& data) {}
+ virtual void addLayer(const DRW_Layer& data) {}
+ virtual void addDimStyle(const DRW_Dimstyle& data) {}
+ virtual void addVport(const DRW_Vport& data) {}
+ virtual void addTextStyle(const DRW_Textstyle& data) {}
+ virtual void addAppId(const DRW_AppId& data) {}
+ virtual void addBlock(const DRW_Block& data) {}
+ virtual void setBlock(const int handle) {}
+ virtual void endBlock() {}
+ virtual void addPoint(const DRW_Point& data) {}
+ virtual void addLine(const DRW_Line& data) {}
+ virtual void addRay(const DRW_Ray& data) {}
+ virtual void addXline(const DRW_Xline& data) {}
+ virtual void addArc(const DRW_Arc& data) {}
+ virtual void addCircle(const DRW_Circle& data) {}
+ virtual void addEllipse(const DRW_Ellipse& data) {}
+ virtual void addLWPolyline(const DRW_LWPolyline& data) {}
+ virtual void addPolyline(const DRW_Polyline& data) {}
+ virtual void addSpline(const DRW_Spline* data) {}
+ virtual void addKnot(const DRW_Entity& data) {}
+ virtual void addInsert(const DRW_Insert& data) {}
+ virtual void addTrace(const DRW_Trace& data) {}
+ virtual void add3dFace(const DRW_3Dface& data) {}
+ virtual void addSolid(const DRW_Solid& data) {}
+ virtual void addMText(const DRW_MText& data) {}
+ virtual void addText(const DRW_Text& data) {}
+ virtual void addDimAlign(const DRW_DimAligned *data) {}
+ virtual void addDimLinear(const DRW_DimLinear *data) {}
+ virtual void addDimRadial(const DRW_DimRadial *data) {}
+ virtual void addDimDiametric(const DRW_DimDiametric *data) {}
+ virtual void addDimAngular(const DRW_DimAngular *data) {}
+ virtual void addDimAngular3P(const DRW_DimAngular3p *data) {}
+ virtual void addDimOrdinate(const DRW_DimOrdinate *data) {}
+ virtual void addLeader(const DRW_Leader *data) {}
+ virtual void addHatch(const DRW_Hatch *data) {}
+ virtual void addViewport(const DRW_Viewport& data) {}
+ virtual void addImage(const DRW_Image *data) {}
+ virtual void linkImage(const DRW_ImageDef *data) {}
+ virtual void addComment(const char* comment) {}
+
+ virtual void writeHeader(DRW_Header& data) {}
+ virtual void writeBlocks() {}
+ virtual void writeBlockRecords() {}
+
+ virtual void writeEntities() {
+ // Write a point
+ DRW_Point point;
+ point.basePoint.x = 10.0;
+ point.basePoint.y = 20.0;
+ point.basePoint.z = 0.0;
+ dxfWriter->writePoint(&point);
+
+ // Write a line
+ DRW_Line line;
+ line.basePoint.x = 0.0;
+ line.basePoint.y = 0.0;
+ line.basePoint.z = 0.0;
+ line.secPoint.x = 100.0;
+ line.secPoint.y = 100.0;
+ line.secPoint.z = 0.0;
+ dxfWriter->writeLine(&line);
+
+ // Write a circle
+ DRW_Circle circle;
+ circle.basePoint.x = 50.0;
+ circle.basePoint.y = 50.0;
+ circle.basePoint.z = 0.0;
+ circle.radious = 25.0;
+ dxfWriter->writeCircle(&circle);
+ }
+
+ virtual void writeLTypes() {}
+ virtual void writeLayers() {}
+ virtual void writeTextstyles() {}
+ virtual void writeVports() {}
+ virtual void writeDimstyles() {}
+ virtual void writeAppId() {}
+
+ dxfRW* dxfWriter;
+ };
+
+ WriterInterface writer;
+ writer.dxfWriter = &dxf;
+
+ bool success = dxf.write(&writer, DRW::AC1015, false); // R2000, ASCII
+
+ if (success) {
+ std::cout << "✓ DXF file written successfully: " << filename << std::endl;
+ } else {
+ std::cout << "✗ Failed to write DXF file" << std::endl;
+ }
+
+ return success;
+}
+
+bool testReadBasicDXF() {
+ std::cout << "\n=== Test: Read Basic DXF ===" << std::endl;
+
+ const char* filename = "test_output.dxf";
+ dxfRW dxf(filename);
+
+ TestInterface reader;
+
+ bool success = dxf.read(&reader, false);
+
+ if (success) {
+ std::cout << "✓ DXF file read successfully" << std::endl;
+ std::cout << " Points read: " << reader.pointCount << std::endl;
+ std::cout << " Lines read: " << reader.lineCount << std::endl;
+ std::cout << " Circles read: " << reader.circleCount << std::endl;
+
+ // Validate counts
+ if (reader.pointCount == 1 && reader.lineCount == 1 && reader.circleCount == 1) {
+ std::cout << "✓ Entity counts match expected values" << std::endl;
+ } else {
+ std::cout << "✗ Entity counts don't match (expected 1 point, 1 line, 1 circle)" << std::endl;
+ return false;
+ }
+ } else {
+ std::cout << "✗ Failed to read DXF file" << std::endl;
+ }
+
+ return success;
+}
+
+bool testBinaryDXF() {
+ std::cout << "\n=== Test: Binary DXF ===" << std::endl;
+
+ const char* filename = "test_binary.dxf";
+ dxfRW dxf(filename);
+
+ // Create a simple writer interface
+ class WriterInterface : public DRW_Interface {
+ public:
+ virtual void addHeader(const DRW_Header* data) {}
+ virtual void addLType(const DRW_LType& data) {}
+ virtual void addLayer(const DRW_Layer& data) {}
+ virtual void addDimStyle(const DRW_Dimstyle& data) {}
+ virtual void addVport(const DRW_Vport& data) {}
+ virtual void addTextStyle(const DRW_Textstyle& data) {}
+ virtual void addAppId(const DRW_AppId& data) {}
+ virtual void addBlock(const DRW_Block& data) {}
+ virtual void setBlock(const int handle) {}
+ virtual void endBlock() {}
+ virtual void addPoint(const DRW_Point& data) {}
+ virtual void addLine(const DRW_Line& data) {}
+ virtual void addRay(const DRW_Ray& data) {}
+ virtual void addXline(const DRW_Xline& data) {}
+ virtual void addArc(const DRW_Arc& data) {}
+ virtual void addCircle(const DRW_Circle& data) {}
+ virtual void addEllipse(const DRW_Ellipse& data) {}
+ virtual void addLWPolyline(const DRW_LWPolyline& data) {}
+ virtual void addPolyline(const DRW_Polyline& data) {}
+ virtual void addSpline(const DRW_Spline* data) {}
+ virtual void addKnot(const DRW_Entity& data) {}
+ virtual void addInsert(const DRW_Insert& data) {}
+ virtual void addTrace(const DRW_Trace& data) {}
+ virtual void add3dFace(const DRW_3Dface& data) {}
+ virtual void addSolid(const DRW_Solid& data) {}
+ virtual void addMText(const DRW_MText& data) {}
+ virtual void addText(const DRW_Text& data) {}
+ virtual void addDimAlign(const DRW_DimAligned *data) {}
+ virtual void addDimLinear(const DRW_DimLinear *data) {}
+ virtual void addDimRadial(const DRW_DimRadial *data) {}
+ virtual void addDimDiametric(const DRW_DimDiametric *data) {}
+ virtual void addDimAngular(const DRW_DimAngular *data) {}
+ virtual void addDimAngular3P(const DRW_DimAngular3p *data) {}
+ virtual void addDimOrdinate(const DRW_DimOrdinate *data) {}
+ virtual void addLeader(const DRW_Leader *data) {}
+ virtual void addHatch(const DRW_Hatch *data) {}
+ virtual void addViewport(const DRW_Viewport& data) {}
+ virtual void addImage(const DRW_Image *data) {}
+ virtual void linkImage(const DRW_ImageDef *data) {}
+ virtual void addComment(const char* comment) {}
+
+ virtual void writeHeader(DRW_Header& data) {}
+ virtual void writeBlocks() {}
+ virtual void writeBlockRecords() {}
+
+ virtual void writeEntities() {
+ DRW_Line line;
+ line.basePoint.x = 0.0;
+ line.basePoint.y = 0.0;
+ line.basePoint.z = 0.0;
+ line.secPoint.x = 50.0;
+ line.secPoint.y = 50.0;
+ line.secPoint.z = 0.0;
+ dxfWriter->writeLine(&line);
+ }
+
+ virtual void writeLTypes() {}
+ virtual void writeLayers() {}
+ virtual void writeTextstyles() {}
+ virtual void writeVports() {}
+ virtual void writeDimstyles() {}
+ virtual void writeAppId() {}
+
+ dxfRW* dxfWriter;
+ };
+
+ WriterInterface writer;
+ writer.dxfWriter = &dxf;
+
+ bool success = dxf.write(&writer, DRW::AC1015, true); // R2000, Binary
+
+ if (success) {
+ std::cout << "✓ Binary DXF file written successfully" << std::endl;
+
+ // Try to read it back
+ TestInterface reader;
+ dxfRW dxfRead(filename);
+ if (dxfRead.read(&reader, false)) {
+ std::cout << "✓ Binary DXF file read successfully" << std::endl;
+ std::cout << " Lines read: " << reader.lineCount << std::endl;
+ } else {
+ std::cout << "✗ Failed to read binary DXF file" << std::endl;
+ return false;
+ }
+ } else {
+ std::cout << "✗ Failed to write binary DXF file" << std::endl;
+ return false;
+ }
+
+ return success;
+}
+
+int main(int argc, char* argv[]) {
+ std::cout << "libdxfrw Basic Tests" << std::endl;
+ std::cout << "===================" << std::endl;
+
+ int failedTests = 0;
+ int totalTests = 0;
+
+ // Test 1: Write a basic DXF file
+ totalTests++;
+ if (!testWriteBasicDXF()) {
+ failedTests++;
+ }
+
+ // Test 2: Read the DXF file back
+ totalTests++;
+ if (!testReadBasicDXF()) {
+ failedTests++;
+ }
+
+ // Test 3: Binary DXF
+ totalTests++;
+ if (!testBinaryDXF()) {
+ failedTests++;
+ }
+
+ // Clean up test files
+ std::remove("test_output.dxf");
+ std::remove("test_binary.dxf");
+
+ std::cout << "\n===================" << std::endl;
+ std::cout << "Tests: " << (totalTests - failedTests) << "/" << totalTests << " passed" << std::endl;
+
+ if (failedTests > 0) {
+ std::cout << "✗ " << failedTests << " test(s) failed" << std::endl;
+ return 1;
+ } else {
+ std::cout << "✓ All tests passed!" << std::endl;
+ return 0;
+ }
+}
diff --git a/tests/test_blocks.cpp b/tests/test_blocks.cpp
new file mode 100644
index 0000000..3c5d97a
--- /dev/null
+++ b/tests/test_blocks.cpp
@@ -0,0 +1,358 @@
+/******************************************************************************
+** libDXFrw - Block Tests **
+** **
+** Copyright (C) 2025 libdxfrw contributors **
+** **
+** This library is free software, licensed under the terms of the GNU **
+** General Public License as published by the Free Software Foundation, **
+** either version 2 of the License, or (at your option) any later version. **
+** You should have received a copy of the GNU General Public License **
+** along with this program. If not, see . **
+******************************************************************************/
+
+#include "libdxfrw.h"
+#include "test_interface.h"
+#include
+#include
+
+bool testBasicBlock() {
+ std::cout << "\n=== Test: Basic Block Definition ===" << std::endl;
+
+ const char* filename = "test_block.dxf";
+
+ // Write block
+ {
+ dxfRW dxf(filename);
+ class BlockWriter : public TestInterface {
+ public:
+ int blockCount = 0;
+
+ virtual void writeBlockRecords() {
+ dxfWriter->writeBlockRecord("SimpleBlock");
+ }
+
+ virtual void writeBlocks() {
+ // Define a simple block with a circle
+ DRW_Block block;
+ block.name = "SimpleBlock";
+ block.basePoint.x = 0.0;
+ block.basePoint.y = 0.0;
+ block.basePoint.z = 0.0;
+ dxfWriter->writeBlock(&block);
+
+ // Add entities to block
+ DRW_Circle circle;
+ circle.basePoint.x = 0.0;
+ circle.basePoint.y = 0.0;
+ circle.basePoint.z = 0.0;
+ circle.radious = 10.0;
+ dxfWriter->writeCircle(&circle);
+
+ DRW_Line line1;
+ line1.basePoint.x = -10.0;
+ line1.basePoint.y = 0.0;
+ line1.basePoint.z = 0.0;
+ line1.secPoint.x = 10.0;
+ line1.secPoint.y = 0.0;
+ line1.secPoint.z = 0.0;
+ dxfWriter->writeLine(&line1);
+
+ DRW_Line line2;
+ line2.basePoint.x = 0.0;
+ line2.basePoint.y = -10.0;
+ line2.basePoint.z = 0.0;
+ line2.secPoint.x = 0.0;
+ line2.secPoint.y = 10.0;
+ line2.secPoint.z = 0.0;
+ dxfWriter->writeLine(&line2);
+ }
+
+ virtual void writeEntities() {
+ // Insert block instance
+ DRW_Insert insert;
+ insert.name = "SimpleBlock";
+ insert.basePoint.x = 50.0;
+ insert.basePoint.y = 50.0;
+ insert.basePoint.z = 0.0;
+ insert.xscale = 1.0;
+ insert.yscale = 1.0;
+ insert.zscale = 1.0;
+ insert.angle = 0.0;
+ dxfWriter->writeInsert(&insert);
+ }
+
+ dxfRW* dxfWriter;
+ };
+
+ BlockWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write block" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read block" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.insertCount != 1) {
+ std::cout << "✗ Expected 1 block insert, got " << reader.insertCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Basic block test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testMultipleBlockInserts() {
+ std::cout << "\n=== Test: Multiple Block Inserts ===" << std::endl;
+
+ const char* filename = "test_multiple_blocks.dxf";
+
+ // Write multiple block instances
+ {
+ dxfRW dxf(filename);
+ class MultiBlockWriter : public TestInterface {
+ public:
+ virtual void writeBlockRecords() {
+ dxfWriter->writeBlockRecord("Square");
+ }
+
+ virtual void writeBlocks() {
+ // Define a square block
+ DRW_Block block;
+ block.name = "Square";
+ block.basePoint.x = 0.0;
+ block.basePoint.y = 0.0;
+ block.basePoint.z = 0.0;
+ dxfWriter->writeBlock(&block);
+
+ // Square outline
+ DRW_LWPolyline square;
+ square.vertlist.push_back(new DRW_Vertex2D(0.0, 0.0, 0.0));
+ square.vertlist.push_back(new DRW_Vertex2D(10.0, 0.0, 0.0));
+ square.vertlist.push_back(new DRW_Vertex2D(10.0, 10.0, 0.0));
+ square.vertlist.push_back(new DRW_Vertex2D(0.0, 10.0, 0.0));
+ square.flags = 1; // Closed
+ dxfWriter->writeLWPolyline(&square);
+
+ for (size_t i = 0; i < square.vertlist.size(); i++) {
+ delete square.vertlist[i];
+ }
+ }
+
+ virtual void writeEntities() {
+ // Insert block at different positions and scales
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ DRW_Insert insert;
+ insert.name = "Square";
+ insert.basePoint.x = i * 30.0;
+ insert.basePoint.y = j * 30.0;
+ insert.basePoint.z = 0.0;
+ insert.xscale = 1.0 + i * 0.5;
+ insert.yscale = 1.0 + j * 0.5;
+ insert.zscale = 1.0;
+ insert.angle = 0.0;
+ dxfWriter->writeInsert(&insert);
+ }
+ }
+ }
+
+ dxfRW* dxfWriter;
+ };
+
+ MultiBlockWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write multiple blocks" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read multiple blocks" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.insertCount != 9) {
+ std::cout << "✗ Expected 9 block inserts, got " << reader.insertCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Multiple block inserts test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testRotatedAndScaledBlocks() {
+ std::cout << "\n=== Test: Rotated and Scaled Blocks ===" << std::endl;
+
+ const char* filename = "test_rotated_blocks.dxf";
+
+ // Write rotated and scaled blocks
+ {
+ dxfRW dxf(filename);
+ class RotatedBlockWriter : public TestInterface {
+ public:
+ virtual void writeBlockRecords() {
+ dxfWriter->writeBlockRecord("Arrow");
+ }
+
+ virtual void writeBlocks() {
+ // Define an arrow block
+ DRW_Block block;
+ block.name = "Arrow";
+ block.basePoint.x = 0.0;
+ block.basePoint.y = 0.0;
+ block.basePoint.z = 0.0;
+ dxfWriter->writeBlock(&block);
+
+ // Arrow shaft
+ DRW_Line shaft;
+ shaft.basePoint.x = 0.0;
+ shaft.basePoint.y = 0.0;
+ shaft.basePoint.z = 0.0;
+ shaft.secPoint.x = 20.0;
+ shaft.secPoint.y = 0.0;
+ shaft.secPoint.z = 0.0;
+ dxfWriter->writeLine(&shaft);
+
+ // Arrow head
+ DRW_Line head1;
+ head1.basePoint.x = 20.0;
+ head1.basePoint.y = 0.0;
+ head1.basePoint.z = 0.0;
+ head1.secPoint.x = 15.0;
+ head1.secPoint.y = 3.0;
+ head1.secPoint.z = 0.0;
+ dxfWriter->writeLine(&head1);
+
+ DRW_Line head2;
+ head2.basePoint.x = 20.0;
+ head2.basePoint.y = 0.0;
+ head2.basePoint.z = 0.0;
+ head2.secPoint.x = 15.0;
+ head2.secPoint.y = -3.0;
+ head2.secPoint.z = 0.0;
+ dxfWriter->writeLine(&head2);
+ }
+
+ virtual void writeEntities() {
+ // Insert arrow at 0 degrees
+ DRW_Insert insert1;
+ insert1.name = "Arrow";
+ insert1.basePoint.x = 50.0;
+ insert1.basePoint.y = 50.0;
+ insert1.basePoint.z = 0.0;
+ insert1.xscale = 1.0;
+ insert1.yscale = 1.0;
+ insert1.zscale = 1.0;
+ insert1.angle = 0.0;
+ dxfWriter->writeInsert(&insert1);
+
+ // Insert arrow at 90 degrees
+ DRW_Insert insert2;
+ insert2.name = "Arrow";
+ insert2.basePoint.x = 100.0;
+ insert2.basePoint.y = 50.0;
+ insert2.basePoint.z = 0.0;
+ insert2.xscale = 1.0;
+ insert2.yscale = 1.0;
+ insert2.zscale = 1.0;
+ insert2.angle = M_PI / 2.0; // 90 degrees
+ dxfWriter->writeInsert(&insert2);
+
+ // Insert scaled arrow at 45 degrees
+ DRW_Insert insert3;
+ insert3.name = "Arrow";
+ insert3.basePoint.x = 75.0;
+ insert3.basePoint.y = 75.0;
+ insert3.basePoint.z = 0.0;
+ insert3.xscale = 2.0;
+ insert3.yscale = 2.0;
+ insert3.zscale = 1.0;
+ insert3.angle = M_PI / 4.0; // 45 degrees
+ dxfWriter->writeInsert(&insert3);
+ }
+
+ dxfRW* dxfWriter;
+ };
+
+ RotatedBlockWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write rotated blocks" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read rotated blocks" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.insertCount != 3) {
+ std::cout << "✗ Expected 3 block inserts, got " << reader.insertCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Rotated and scaled blocks test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ std::cout << "libdxfrw Block Tests" << std::endl;
+ std::cout << "====================" << std::endl;
+
+ int failedTests = 0;
+ int totalTests = 0;
+
+ totalTests++;
+ if (!testBasicBlock()) failedTests++;
+
+ totalTests++;
+ if (!testMultipleBlockInserts()) failedTests++;
+
+ totalTests++;
+ if (!testRotatedAndScaledBlocks()) failedTests++;
+
+ std::cout << "\n====================" << std::endl;
+ std::cout << "Tests: " << (totalTests - failedTests) << "/" << totalTests << " passed" << std::endl;
+
+ if (failedTests > 0) {
+ std::cout << "✗ " << failedTests << " test(s) failed" << std::endl;
+ return 1;
+ } else {
+ std::cout << "✓ All block tests passed!" << std::endl;
+ return 0;
+ }
+}
diff --git a/tests/test_entities.cpp b/tests/test_entities.cpp
new file mode 100644
index 0000000..e776b94
--- /dev/null
+++ b/tests/test_entities.cpp
@@ -0,0 +1,453 @@
+/******************************************************************************
+** libDXFrw - Entity Tests **
+** **
+** Copyright (C) 2025 libdxfrw contributors **
+** **
+** This library is free software, licensed under the terms of the GNU **
+** General Public License as published by the Free Software Foundation, **
+** either version 2 of the License, or (at your option) any later version. **
+** You should have received a copy of the GNU General Public License **
+** along with this program. If not, see . **
+******************************************************************************/
+
+#include "libdxfrw.h"
+#include "test_interface.h"
+#include
+#include
+#include
+
+// Helper to compare doubles
+bool doubleEquals(double a, double b, double epsilon = 0.0001) {
+ return std::abs(a - b) < epsilon;
+}
+
+bool testArcEntity() {
+ std::cout << "\n=== Test: Arc Entity ===" << std::endl;
+
+ const char* filename = "test_arc.dxf";
+
+ // Write arc
+ {
+ dxfRW dxf(filename);
+ class ArcWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_Arc arc;
+ arc.basePoint.x = 100.0;
+ arc.basePoint.y = 100.0;
+ arc.basePoint.z = 0.0;
+ arc.radious = 50.0;
+ arc.staangle = 0.0; // 0 degrees
+ arc.endangle = M_PI / 2.0; // 90 degrees
+ dxfWriter->writeArc(&arc);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ ArcWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write arc" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify arc
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read arc" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.arcCount != 1) {
+ std::cout << "✗ Expected 1 arc, got " << reader.arcCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Arc entity test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testEllipseEntity() {
+ std::cout << "\n=== Test: Ellipse Entity ===" << std::endl;
+
+ const char* filename = "test_ellipse.dxf";
+
+ // Write ellipse
+ {
+ dxfRW dxf(filename);
+ class EllipseWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_Ellipse ellipse;
+ ellipse.basePoint.x = 50.0;
+ ellipse.basePoint.y = 50.0;
+ ellipse.basePoint.z = 0.0;
+ ellipse.secPoint.x = 30.0; // Major axis endpoint
+ ellipse.secPoint.y = 0.0;
+ ellipse.secPoint.z = 0.0;
+ ellipse.ratio = 0.5; // Minor/major axis ratio
+ ellipse.staparam = 0.0;
+ ellipse.endparam = 2.0 * M_PI;
+ dxfWriter->writeEllipse(&ellipse);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ EllipseWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write ellipse" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify ellipse
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read ellipse" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.ellipseCount != 1) {
+ std::cout << "✗ Expected 1 ellipse, got " << reader.ellipseCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Ellipse entity test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testRayAndXlineEntities() {
+ std::cout << "\n=== Test: Ray and XLine Entities ===" << std::endl;
+
+ const char* filename = "test_ray_xline.dxf";
+
+ // Write ray and xline
+ {
+ dxfRW dxf(filename);
+ class RayXlineWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ // Write ray
+ DRW_Ray ray;
+ ray.basePoint.x = 10.0;
+ ray.basePoint.y = 10.0;
+ ray.basePoint.z = 0.0;
+ ray.secPoint.x = 1.0; // Direction vector
+ ray.secPoint.y = 1.0;
+ ray.secPoint.z = 0.0;
+ dxfWriter->writeRay(&ray);
+
+ // Write xline (infinite line)
+ DRW_Xline xline;
+ xline.basePoint.x = 20.0;
+ xline.basePoint.y = 20.0;
+ xline.basePoint.z = 0.0;
+ xline.secPoint.x = 0.0; // Direction vector
+ xline.secPoint.y = 1.0;
+ xline.secPoint.z = 0.0;
+ dxfWriter->writeXline(&xline);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ RayXlineWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write ray/xline" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read ray/xline" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.rayCount != 1 || reader.xlineCount != 1) {
+ std::cout << "✗ Expected 1 ray and 1 xline, got " << reader.rayCount
+ << " rays and " << reader.xlineCount << " xlines" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Ray and XLine entity test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testTraceAndSolidEntities() {
+ std::cout << "\n=== Test: Trace and Solid Entities ===" << std::endl;
+
+ const char* filename = "test_trace_solid.dxf";
+
+ // Write trace and solid
+ {
+ dxfRW dxf(filename);
+ class TraceSolidWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ // Write trace (filled triangle/quad)
+ DRW_Trace trace;
+ trace.basePoint.x = 0.0;
+ trace.basePoint.y = 0.0;
+ trace.basePoint.z = 0.0;
+ trace.secPoint.x = 10.0;
+ trace.secPoint.y = 0.0;
+ trace.secPoint.z = 0.0;
+ trace.thirdPoint.x = 10.0;
+ trace.thirdPoint.y = 10.0;
+ trace.thirdPoint.z = 0.0;
+ trace.fourPoint.x = 0.0;
+ trace.fourPoint.y = 10.0;
+ trace.fourPoint.z = 0.0;
+ dxfWriter->writeTrace(&trace);
+
+ // Write solid
+ DRW_Solid solid;
+ solid.basePoint.x = 20.0;
+ solid.basePoint.y = 0.0;
+ solid.basePoint.z = 0.0;
+ solid.secPoint.x = 30.0;
+ solid.secPoint.y = 0.0;
+ solid.secPoint.z = 0.0;
+ solid.thirdPoint.x = 30.0;
+ solid.thirdPoint.y = 10.0;
+ solid.thirdPoint.z = 0.0;
+ solid.fourPoint.x = 20.0;
+ solid.fourPoint.y = 10.0;
+ solid.fourPoint.z = 0.0;
+ dxfWriter->writeSolid(&solid);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ TraceSolidWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write trace/solid" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read trace/solid" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.traceCount != 1 || reader.solidCount != 1) {
+ std::cout << "✗ Expected 1 trace and 1 solid, got " << reader.traceCount
+ << " traces and " << reader.solidCount << " solids" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Trace and Solid entity test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool test3DFaceEntity() {
+ std::cout << "\n=== Test: 3D Face Entity ===" << std::endl;
+
+ const char* filename = "test_3dface.dxf";
+
+ // Write 3D face
+ {
+ dxfRW dxf(filename);
+ class Face3DWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_3Dface face;
+ face.basePoint.x = 0.0;
+ face.basePoint.y = 0.0;
+ face.basePoint.z = 0.0;
+ face.secPoint.x = 10.0;
+ face.secPoint.y = 0.0;
+ face.secPoint.z = 5.0;
+ face.thirdPoint.x = 10.0;
+ face.thirdPoint.y = 10.0;
+ face.thirdPoint.z = 10.0;
+ face.fourPoint.x = 0.0;
+ face.fourPoint.y = 10.0;
+ face.fourPoint.z = 5.0;
+ dxfWriter->write3dface(&face);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ Face3DWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write 3D face" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read 3D face" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.face3dCount != 1) {
+ std::cout << "✗ Expected 1 3D face, got " << reader.face3dCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ 3D Face entity test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testMultipleEntities() {
+ std::cout << "\n=== Test: Multiple Entities ===" << std::endl;
+
+ const char* filename = "test_multiple.dxf";
+
+ // Write multiple entities
+ {
+ dxfRW dxf(filename);
+ class MultiWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ // Multiple points
+ for (int i = 0; i < 5; i++) {
+ DRW_Point point;
+ point.basePoint.x = i * 10.0;
+ point.basePoint.y = i * 10.0;
+ point.basePoint.z = 0.0;
+ dxfWriter->writePoint(&point);
+ }
+
+ // Multiple lines
+ for (int i = 0; i < 3; i++) {
+ DRW_Line line;
+ line.basePoint.x = i * 20.0;
+ line.basePoint.y = 0.0;
+ line.basePoint.z = 0.0;
+ line.secPoint.x = i * 20.0;
+ line.secPoint.y = 50.0;
+ line.secPoint.z = 0.0;
+ dxfWriter->writeLine(&line);
+ }
+
+ // Multiple circles
+ for (int i = 0; i < 4; i++) {
+ DRW_Circle circle;
+ circle.basePoint.x = i * 30.0;
+ circle.basePoint.y = 100.0;
+ circle.basePoint.z = 0.0;
+ circle.radious = 10.0 + i * 5.0;
+ dxfWriter->writeCircle(&circle);
+ }
+ }
+ dxfRW* dxfWriter;
+ };
+
+ MultiWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write multiple entities" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read multiple entities" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.pointCount != 5 || reader.lineCount != 3 || reader.circleCount != 4) {
+ std::cout << "✗ Expected 5 points, 3 lines, 4 circles, got "
+ << reader.pointCount << " points, "
+ << reader.lineCount << " lines, "
+ << reader.circleCount << " circles" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Multiple entities test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ std::cout << "libdxfrw Entity Tests" << std::endl;
+ std::cout << "=====================" << std::endl;
+
+ int failedTests = 0;
+ int totalTests = 0;
+
+ totalTests++;
+ if (!testArcEntity()) failedTests++;
+
+ totalTests++;
+ if (!testEllipseEntity()) failedTests++;
+
+ totalTests++;
+ if (!testRayAndXlineEntities()) failedTests++;
+
+ totalTests++;
+ if (!testTraceAndSolidEntities()) failedTests++;
+
+ totalTests++;
+ if (!test3DFaceEntity()) failedTests++;
+
+ totalTests++;
+ if (!testMultipleEntities()) failedTests++;
+
+ std::cout << "\n=====================" << std::endl;
+ std::cout << "Tests: " << (totalTests - failedTests) << "/" << totalTests << " passed" << std::endl;
+
+ if (failedTests > 0) {
+ std::cout << "✗ " << failedTests << " test(s) failed" << std::endl;
+ return 1;
+ } else {
+ std::cout << "✓ All entity tests passed!" << std::endl;
+ return 0;
+ }
+}
diff --git a/tests/test_errors.cpp b/tests/test_errors.cpp
new file mode 100644
index 0000000..b6bc33b
--- /dev/null
+++ b/tests/test_errors.cpp
@@ -0,0 +1,286 @@
+/******************************************************************************
+** libDXFrw - Error Handling Tests **
+** **
+** Copyright (C) 2025 libdxfrw contributors **
+** **
+** This library is free software, licensed under the terms of the GNU **
+** General Public License as published by the Free Software Foundation, **
+** either version 2 of the License, or (at your option) any later version. **
+** You should have received a copy of the GNU General Public License **
+** along with this program. If not, see . **
+******************************************************************************/
+
+#include "libdxfrw.h"
+#include "test_interface.h"
+#include
+#include
+#include
+
+bool testNonExistentFile() {
+ std::cout << "\n=== Test: Read Non-Existent File ===" << std::endl;
+
+ const char* filename = "nonexistent_file.dxf";
+
+ dxfRW dxf(filename);
+ TestInterface reader;
+
+ bool success = dxf.read(&reader, false);
+
+ if (success) {
+ std::cout << "✗ Should have failed reading non-existent file" << std::endl;
+ return false;
+ }
+
+ std::cout << "✓ Correctly failed to read non-existent file" << std::endl;
+ return true;
+}
+
+bool testInvalidDXFFile() {
+ std::cout << "\n=== Test: Read Invalid DXF File ===" << std::endl;
+
+ const char* filename = "invalid.dxf";
+
+ // Create an invalid DXF file
+ {
+ std::ofstream file(filename);
+ file << "This is not a valid DXF file\n";
+ file << "Just random text\n";
+ file.close();
+ }
+
+ dxfRW dxf(filename);
+ TestInterface reader;
+
+ bool success = dxf.read(&reader, false);
+
+ std::remove(filename);
+
+ // The library may handle invalid files gracefully without strict validation
+ // We just verify it doesn't crash and completes
+ std::cout << "✓ Invalid DXF file handled without crash (success=" << success << ")" << std::endl;
+ return true;
+}
+
+bool testEmptyDXFFile() {
+ std::cout << "\n=== Test: Read Empty DXF File ===" << std::endl;
+
+ const char* filename = "empty.dxf";
+
+ // Create an empty file
+ {
+ std::ofstream file(filename);
+ file.close();
+ }
+
+ dxfRW dxf(filename);
+ TestInterface reader;
+
+ bool success = dxf.read(&reader, false);
+
+ std::remove(filename);
+
+ // The library may handle empty files gracefully
+ // We just verify it doesn't crash
+ std::cout << "✓ Empty DXF file handled without crash (success=" << success << ")" << std::endl;
+ return true;
+}
+
+bool testWriteToReadOnlyLocation() {
+ std::cout << "\n=== Test: Write to Invalid Path ===" << std::endl;
+
+ // Try to write to an invalid path
+ const char* filename = "/nonexistent_directory_12345/test.dxf";
+
+ dxfRW dxf(filename);
+ class SimpleWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_Point point;
+ point.basePoint.x = 0.0;
+ point.basePoint.y = 0.0;
+ point.basePoint.z = 0.0;
+ dxfWriter->writePoint(&point);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ SimpleWriter writer;
+ writer.dxfWriter = &dxf;
+
+ bool success = dxf.write(&writer, DRW::AC1015, false);
+
+ // The library may handle this gracefully or fail
+ // We just verify it doesn't crash
+ std::cout << "✓ Invalid path handled without crash (success=" << success << ")" << std::endl;
+ return true;
+}
+
+bool testEmptyEntityLists() {
+ std::cout << "\n=== Test: Write File with No Entities ===" << std::endl;
+
+ const char* filename = "test_no_entities.dxf";
+
+ // Write file with no entities
+ {
+ dxfRW dxf(filename);
+ class EmptyWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ // Don't write any entities
+ }
+ dxfRW* dxfWriter;
+ };
+
+ EmptyWriter writer;
+ writer.dxfWriter = &dxf;
+
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write empty DXF file" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read empty DXF file" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.pointCount != 0 && reader.lineCount != 0 && reader.circleCount != 0) {
+ std::cout << "✗ Empty file should have no entities" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Empty entity list test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testBoundaryValues() {
+ std::cout << "\n=== Test: Boundary Values ===" << std::endl;
+
+ const char* filename = "test_boundary.dxf";
+
+ // Write entities with extreme values
+ {
+ dxfRW dxf(filename);
+ class BoundaryWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ // Very large coordinates
+ DRW_Point largePoint;
+ largePoint.basePoint.x = 1e10;
+ largePoint.basePoint.y = 1e10;
+ largePoint.basePoint.z = 1e10;
+ dxfWriter->writePoint(&largePoint);
+
+ // Very small coordinates (near zero)
+ DRW_Point smallPoint;
+ smallPoint.basePoint.x = 1e-10;
+ smallPoint.basePoint.y = 1e-10;
+ smallPoint.basePoint.z = 1e-10;
+ dxfWriter->writePoint(&smallPoint);
+
+ // Negative coordinates
+ DRW_Point negativePoint;
+ negativePoint.basePoint.x = -1000.0;
+ negativePoint.basePoint.y = -1000.0;
+ negativePoint.basePoint.z = -1000.0;
+ dxfWriter->writePoint(&negativePoint);
+
+ // Very small circle
+ DRW_Circle tinyCircle;
+ tinyCircle.basePoint.x = 0.0;
+ tinyCircle.basePoint.y = 0.0;
+ tinyCircle.basePoint.z = 0.0;
+ tinyCircle.radious = 0.001;
+ dxfWriter->writeCircle(&tinyCircle);
+
+ // Very large circle
+ DRW_Circle hugeCircle;
+ hugeCircle.basePoint.x = 0.0;
+ hugeCircle.basePoint.y = 0.0;
+ hugeCircle.basePoint.z = 0.0;
+ hugeCircle.radious = 1e6;
+ dxfWriter->writeCircle(&hugeCircle);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ BoundaryWriter writer;
+ writer.dxfWriter = &dxf;
+
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write boundary values" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read boundary values" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.pointCount != 3 || reader.circleCount != 2) {
+ std::cout << "✗ Expected 3 points and 2 circles" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Boundary values test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ std::cout << "libdxfrw Error Handling Tests" << std::endl;
+ std::cout << "==============================" << std::endl;
+
+ int failedTests = 0;
+ int totalTests = 0;
+
+ totalTests++;
+ if (!testNonExistentFile()) failedTests++;
+
+ totalTests++;
+ if (!testInvalidDXFFile()) failedTests++;
+
+ totalTests++;
+ if (!testEmptyDXFFile()) failedTests++;
+
+ totalTests++;
+ if (!testWriteToReadOnlyLocation()) failedTests++;
+
+ totalTests++;
+ if (!testEmptyEntityLists()) failedTests++;
+
+ totalTests++;
+ if (!testBoundaryValues()) failedTests++;
+
+ std::cout << "\n==============================" << std::endl;
+ std::cout << "Tests: " << (totalTests - failedTests) << "/" << totalTests << " passed" << std::endl;
+
+ if (failedTests > 0) {
+ std::cout << "✗ " << failedTests << " test(s) failed" << std::endl;
+ return 1;
+ } else {
+ std::cout << "✓ All error handling tests passed!" << std::endl;
+ return 0;
+ }
+}
diff --git a/tests/test_interface.h b/tests/test_interface.h
new file mode 100644
index 0000000..7c94c6e
--- /dev/null
+++ b/tests/test_interface.h
@@ -0,0 +1,186 @@
+/******************************************************************************
+** libDXFrw - Test Interface **
+** **
+** Copyright (C) 2025 libdxfrw contributors **
+** **
+** This library is free software, licensed under the terms of the GNU **
+** General Public License as published by the Free Software Foundation, **
+** either version 2 of the License, or (at your option) any later version. **
+** You should have received a copy of the GNU General Public License **
+** along with this program. If not, see . **
+******************************************************************************/
+
+#ifndef TEST_INTERFACE_H
+#define TEST_INTERFACE_H
+
+#include "drw_interface.h"
+#include
+#include
+
+class TestInterface : public DRW_Interface {
+public:
+ TestInterface() : pointCount(0), lineCount(0), circleCount(0), arcCount(0),
+ ellipseCount(0), lwPolylineCount(0), polylineCount(0),
+ splineCount(0), textCount(0), mtextCount(0), insertCount(0),
+ rayCount(0), xlineCount(0), traceCount(0), solidCount(0),
+ face3dCount(0), hatchCount(0), layerCount(0), ltypeCount(0) {}
+ ~TestInterface() {}
+
+ // Entity counters for validation
+ int pointCount;
+ int lineCount;
+ int circleCount;
+ int arcCount;
+ int ellipseCount;
+ int lwPolylineCount;
+ int polylineCount;
+ int splineCount;
+ int textCount;
+ int mtextCount;
+ int insertCount;
+ int rayCount;
+ int xlineCount;
+ int traceCount;
+ int solidCount;
+ int face3dCount;
+ int hatchCount;
+
+ // Table object counters
+ int layerCount;
+ int ltypeCount;
+
+ // Implement required virtual methods
+ virtual void addHeader(const DRW_Header* data) {
+ std::cout << "Header added" << std::endl;
+ }
+
+ virtual void addLType(const DRW_LType& data) {
+ ltypeCount++;
+ }
+ virtual void addLayer(const DRW_Layer& data) {
+ layerCount++;
+ }
+ virtual void addDimStyle(const DRW_Dimstyle& data) {}
+ virtual void addVport(const DRW_Vport& data) {}
+ virtual void addTextStyle(const DRW_Textstyle& data) {}
+ virtual void addAppId(const DRW_AppId& data) {}
+
+ virtual void addBlock(const DRW_Block& data) {}
+ virtual void setBlock(const int handle) {}
+ virtual void endBlock() {}
+
+ virtual void addPoint(const DRW_Point& data) {
+ pointCount++;
+ std::cout << "Point added at (" << data.basePoint.x << ", "
+ << data.basePoint.y << ", " << data.basePoint.z << ")" << std::endl;
+ }
+
+ virtual void addLine(const DRW_Line& data) {
+ lineCount++;
+ std::cout << "Line added from (" << data.basePoint.x << ", "
+ << data.basePoint.y << ") to (" << data.secPoint.x << ", "
+ << data.secPoint.y << ")" << std::endl;
+ }
+
+ virtual void addRay(const DRW_Ray& data) {
+ rayCount++;
+ }
+ virtual void addXline(const DRW_Xline& data) {
+ xlineCount++;
+ }
+
+ virtual void addArc(const DRW_Arc& data) {
+ arcCount++;
+ std::cout << "Arc added at (" << data.basePoint.x << ", "
+ << data.basePoint.y << "), radius=" << data.radious << std::endl;
+ }
+
+ virtual void addCircle(const DRW_Circle& data) {
+ circleCount++;
+ std::cout << "Circle added at (" << data.basePoint.x << ", "
+ << data.basePoint.y << "), radius=" << data.radious << std::endl;
+ }
+
+ virtual void addEllipse(const DRW_Ellipse& data) {
+ ellipseCount++;
+ }
+ virtual void addLWPolyline(const DRW_LWPolyline& data) {
+ lwPolylineCount++;
+ }
+ virtual void addPolyline(const DRW_Polyline& data) {
+ polylineCount++;
+ }
+ virtual void addSpline(const DRW_Spline* data) {
+ splineCount++;
+ }
+ virtual void addKnot(const DRW_Entity& data) {}
+ virtual void addInsert(const DRW_Insert& data) {
+ insertCount++;
+ }
+ virtual void addTrace(const DRW_Trace& data) {
+ traceCount++;
+ }
+ virtual void add3dFace(const DRW_3Dface& data) {
+ face3dCount++;
+ }
+ virtual void addSolid(const DRW_Solid& data) {
+ solidCount++;
+ }
+ virtual void addMText(const DRW_MText& data) {
+ mtextCount++;
+ }
+ virtual void addText(const DRW_Text& data) {
+ textCount++;
+ }
+ virtual void addDimAlign(const DRW_DimAligned *data) {}
+ virtual void addDimLinear(const DRW_DimLinear *data) {}
+ virtual void addDimRadial(const DRW_DimRadial *data) {}
+ virtual void addDimDiametric(const DRW_DimDiametric *data) {}
+ virtual void addDimAngular(const DRW_DimAngular *data) {}
+ virtual void addDimAngular3P(const DRW_DimAngular3p *data) {}
+ virtual void addDimOrdinate(const DRW_DimOrdinate *data) {}
+ virtual void addLeader(const DRW_Leader *data) {}
+ virtual void addHatch(const DRW_Hatch *data) {
+ hatchCount++;
+ }
+ virtual void addViewport(const DRW_Viewport& data) {}
+ virtual void addImage(const DRW_Image *data) {}
+ virtual void linkImage(const DRW_ImageDef *data) {}
+ virtual void addComment(const char* comment) {}
+
+ // Write methods
+ virtual void writeHeader(DRW_Header& data) {}
+ virtual void writeBlocks() {}
+ virtual void writeBlockRecords() {}
+ virtual void writeEntities() {}
+ virtual void writeLTypes() {}
+ virtual void writeLayers() {}
+ virtual void writeTextstyles() {}
+ virtual void writeVports() {}
+ virtual void writeDimstyles() {}
+ virtual void writeAppId() {}
+
+ void reset() {
+ pointCount = 0;
+ lineCount = 0;
+ circleCount = 0;
+ arcCount = 0;
+ ellipseCount = 0;
+ lwPolylineCount = 0;
+ polylineCount = 0;
+ splineCount = 0;
+ textCount = 0;
+ mtextCount = 0;
+ insertCount = 0;
+ rayCount = 0;
+ xlineCount = 0;
+ traceCount = 0;
+ solidCount = 0;
+ face3dCount = 0;
+ hatchCount = 0;
+ layerCount = 0;
+ ltypeCount = 0;
+ }
+};
+
+#endif // TEST_INTERFACE_H
diff --git a/tests/test_polylines.cpp b/tests/test_polylines.cpp
new file mode 100644
index 0000000..84d4326
--- /dev/null
+++ b/tests/test_polylines.cpp
@@ -0,0 +1,371 @@
+/******************************************************************************
+** libDXFrw - Polyline and Spline Tests **
+** **
+** Copyright (C) 2025 libdxfrw contributors **
+** **
+** This library is free software, licensed under the terms of the GNU **
+** General Public License as published by the Free Software Foundation, **
+** either version 2 of the License, or (at your option) any later version. **
+** You should have received a copy of the GNU General Public License **
+** along with this program. If not, see . **
+******************************************************************************/
+
+#include "libdxfrw.h"
+#include "test_interface.h"
+#include
+#include
+#include
+
+bool testLWPolyline() {
+ std::cout << "\n=== Test: LWPolyline (Lightweight Polyline) ===" << std::endl;
+
+ const char* filename = "test_lwpolyline.dxf";
+
+ // Write LWPolyline
+ {
+ dxfRW dxf(filename);
+ class LWPolylineWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_LWPolyline lwpoly;
+ lwpoly.vertlist.push_back(new DRW_Vertex2D(0.0, 0.0, 0.0));
+ lwpoly.vertlist.push_back(new DRW_Vertex2D(100.0, 0.0, 0.0));
+ lwpoly.vertlist.push_back(new DRW_Vertex2D(100.0, 100.0, 0.0));
+ lwpoly.vertlist.push_back(new DRW_Vertex2D(0.0, 100.0, 0.0));
+ lwpoly.flags = 1; // Closed polyline
+ dxfWriter->writeLWPolyline(&lwpoly);
+
+ // Clean up vertices
+ for (size_t i = 0; i < lwpoly.vertlist.size(); i++) {
+ delete lwpoly.vertlist[i];
+ }
+ }
+ dxfRW* dxfWriter;
+ };
+
+ LWPolylineWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write LWPolyline" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read LWPolyline" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.lwPolylineCount != 1) {
+ std::cout << "✗ Expected 1 LWPolyline, got " << reader.lwPolylineCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ LWPolyline test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testPolyline() {
+ std::cout << "\n=== Test: Polyline (3D Polyline) ===" << std::endl;
+
+ const char* filename = "test_polyline.dxf";
+
+ // Write Polyline
+ {
+ dxfRW dxf(filename);
+ class PolylineWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_Polyline poly;
+ poly.flags = 8; // 3D polyline
+ poly.vertlist.push_back(new DRW_Vertex(0.0, 0.0, 0.0, 0.0));
+ poly.vertlist.push_back(new DRW_Vertex(50.0, 0.0, 10.0, 0.0));
+ poly.vertlist.push_back(new DRW_Vertex(50.0, 50.0, 20.0, 0.0));
+ poly.vertlist.push_back(new DRW_Vertex(0.0, 50.0, 10.0, 0.0));
+ dxfWriter->writePolyline(&poly);
+
+ // Clean up vertices
+ for (size_t i = 0; i < poly.vertlist.size(); i++) {
+ delete poly.vertlist[i];
+ }
+ }
+ dxfRW* dxfWriter;
+ };
+
+ PolylineWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write Polyline" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read Polyline" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.polylineCount != 1) {
+ std::cout << "✗ Expected 1 Polyline, got " << reader.polylineCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Polyline test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testSpline() {
+ std::cout << "\n=== Test: Spline ===" << std::endl;
+
+ const char* filename = "test_spline.dxf";
+
+ // Write Spline
+ {
+ dxfRW dxf(filename);
+ class SplineWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_Spline spline;
+ spline.degree = 3; // Cubic spline
+ spline.ncontrol = 5; // Number of control points
+
+ // Add control points
+ spline.controllist.push_back(new DRW_Coord(0.0, 0.0, 0.0));
+ spline.controllist.push_back(new DRW_Coord(25.0, 50.0, 0.0));
+ spline.controllist.push_back(new DRW_Coord(50.0, 50.0, 0.0));
+ spline.controllist.push_back(new DRW_Coord(75.0, 0.0, 0.0));
+ spline.controllist.push_back(new DRW_Coord(100.0, 0.0, 0.0));
+
+ // Add knots
+ spline.nknots = 9; // degree + control points + 1
+ spline.knotslist.push_back(0.0);
+ spline.knotslist.push_back(0.0);
+ spline.knotslist.push_back(0.0);
+ spline.knotslist.push_back(0.0);
+ spline.knotslist.push_back(0.5);
+ spline.knotslist.push_back(1.0);
+ spline.knotslist.push_back(1.0);
+ spline.knotslist.push_back(1.0);
+ spline.knotslist.push_back(1.0);
+
+ dxfWriter->writeSpline(&spline);
+
+ // Clean up
+ for (size_t i = 0; i < spline.controllist.size(); i++) {
+ delete spline.controllist[i];
+ }
+ }
+ dxfRW* dxfWriter;
+ };
+
+ SplineWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write Spline" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read Spline" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.splineCount != 1) {
+ std::cout << "✗ Expected 1 Spline, got " << reader.splineCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Spline test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testClosedLWPolyline() {
+ std::cout << "\n=== Test: Closed LWPolyline ===" << std::endl;
+
+ const char* filename = "test_closed_lwpoly.dxf";
+
+ // Write closed LWPolyline (pentagon)
+ {
+ dxfRW dxf(filename);
+ class ClosedPolyWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_LWPolyline lwpoly;
+ lwpoly.flags = 1; // Closed
+
+ // Pentagon vertices
+ for (int i = 0; i < 5; i++) {
+ double angle = i * 2.0 * M_PI / 5.0;
+ double x = 50.0 + 30.0 * cos(angle);
+ double y = 50.0 + 30.0 * sin(angle);
+ lwpoly.vertlist.push_back(new DRW_Vertex2D(x, y, 0.0));
+ }
+
+ dxfWriter->writeLWPolyline(&lwpoly);
+
+ // Clean up
+ for (size_t i = 0; i < lwpoly.vertlist.size(); i++) {
+ delete lwpoly.vertlist[i];
+ }
+ }
+ dxfRW* dxfWriter;
+ };
+
+ ClosedPolyWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write closed LWPolyline" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read closed LWPolyline" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.lwPolylineCount != 1) {
+ std::cout << "✗ Expected 1 closed LWPolyline, got " << reader.lwPolylineCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Closed LWPolyline test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testPolylineWithBulge() {
+ std::cout << "\n=== Test: LWPolyline with Bulge (Arc Segments) ===" << std::endl;
+
+ const char* filename = "test_bulge_lwpoly.dxf";
+
+ // Write LWPolyline with bulge
+ {
+ dxfRW dxf(filename);
+ class BulgePolyWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_LWPolyline lwpoly;
+
+ // Rectangle with rounded corners using bulge
+ DRW_Vertex2D* v1 = new DRW_Vertex2D(0.0, 0.0, 0.5); // bulge for arc
+ DRW_Vertex2D* v2 = new DRW_Vertex2D(100.0, 0.0, 0.5);
+ DRW_Vertex2D* v3 = new DRW_Vertex2D(100.0, 100.0, 0.5);
+ DRW_Vertex2D* v4 = new DRW_Vertex2D(0.0, 100.0, 0.5);
+
+ lwpoly.vertlist.push_back(v1);
+ lwpoly.vertlist.push_back(v2);
+ lwpoly.vertlist.push_back(v3);
+ lwpoly.vertlist.push_back(v4);
+ lwpoly.flags = 1; // Closed
+
+ dxfWriter->writeLWPolyline(&lwpoly);
+
+ // Clean up
+ for (size_t i = 0; i < lwpoly.vertlist.size(); i++) {
+ delete lwpoly.vertlist[i];
+ }
+ }
+ dxfRW* dxfWriter;
+ };
+
+ BulgePolyWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write LWPolyline with bulge" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read LWPolyline with bulge" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.lwPolylineCount != 1) {
+ std::cout << "✗ Expected 1 LWPolyline with bulge, got " << reader.lwPolylineCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ LWPolyline with bulge test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ std::cout << "libdxfrw Polyline and Spline Tests" << std::endl;
+ std::cout << "===================================" << std::endl;
+
+ int failedTests = 0;
+ int totalTests = 0;
+
+ totalTests++;
+ if (!testLWPolyline()) failedTests++;
+
+ totalTests++;
+ if (!testPolyline()) failedTests++;
+
+ totalTests++;
+ if (!testSpline()) failedTests++;
+
+ totalTests++;
+ if (!testClosedLWPolyline()) failedTests++;
+
+ totalTests++;
+ if (!testPolylineWithBulge()) failedTests++;
+
+ std::cout << "\n===================================" << std::endl;
+ std::cout << "Tests: " << (totalTests - failedTests) << "/" << totalTests << " passed" << std::endl;
+
+ if (failedTests > 0) {
+ std::cout << "✗ " << failedTests << " test(s) failed" << std::endl;
+ return 1;
+ } else {
+ std::cout << "✓ All polyline/spline tests passed!" << std::endl;
+ return 0;
+ }
+}
diff --git a/tests/test_tables.cpp b/tests/test_tables.cpp
new file mode 100644
index 0000000..900339c
--- /dev/null
+++ b/tests/test_tables.cpp
@@ -0,0 +1,320 @@
+/******************************************************************************
+** libDXFrw - Table Objects Tests (Layers, LineTypes, etc.) **
+** **
+** Copyright (C) 2025 libdxfrw contributors **
+** **
+** This library is free software, licensed under the terms of the GNU **
+** General Public License as published by the Free Software Foundation, **
+** either version 2 of the License, or (at your option) any later version. **
+** You should have received a copy of the GNU General Public License **
+** along with this program. If not, see . **
+******************************************************************************/
+
+#include "libdxfrw.h"
+#include "test_interface.h"
+#include
+#include
+
+bool testLayerDefinitions() {
+ std::cout << "\n=== Test: Layer Definitions ===" << std::endl;
+
+ const char* filename = "test_layers.dxf";
+
+ // Write layers
+ {
+ dxfRW dxf(filename);
+ class LayerWriter : public TestInterface {
+ public:
+ virtual void writeLayers() {
+ // Layer 0 (default layer)
+ DRW_Layer layer0;
+ layer0.name = "0";
+ layer0.color = 7; // White
+ layer0.lWeight = DRW_LW_Conv::width00;
+ dxfWriter->writeLayer(&layer0);
+
+ // Construction layer
+ DRW_Layer construction;
+ construction.name = "Construction";
+ construction.color = 8; // Dark gray
+ construction.lineType = "DASHED";
+ construction.lWeight = DRW_LW_Conv::width01;
+ dxfWriter->writeLayer(&construction);
+
+ // Dimension layer
+ DRW_Layer dimensions;
+ dimensions.name = "Dimensions";
+ dimensions.color = 1; // Red
+ dimensions.lineType = "CONTINUOUS";
+ dimensions.lWeight = DRW_LW_Conv::width02;
+ dxfWriter->writeLayer(&dimensions);
+
+ // Hidden layer
+ DRW_Layer hidden;
+ hidden.name = "Hidden";
+ hidden.color = 9;
+ hidden.lineType = "HIDDEN";
+ hidden.flags = 1; // Frozen
+ hidden.lWeight = DRW_LW_Conv::width03;
+ dxfWriter->writeLayer(&hidden);
+ }
+
+ virtual void writeEntities() {
+ // Add a line on layer "Construction"
+ DRW_Line line;
+ line.basePoint.x = 0.0;
+ line.basePoint.y = 0.0;
+ line.basePoint.z = 0.0;
+ line.secPoint.x = 100.0;
+ line.secPoint.y = 100.0;
+ line.secPoint.z = 0.0;
+ line.layer = "Construction";
+ line.color = 256; // ByLayer
+ dxfWriter->writeLine(&line);
+ }
+
+ dxfRW* dxfWriter;
+ };
+
+ LayerWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write layers" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read layers" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.layerCount < 3) { // At least 3 custom layers
+ std::cout << "✗ Expected at least 3 layers, got " << reader.layerCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Layer definitions test passed (" << reader.layerCount << " layers)" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testLineTypes() {
+ std::cout << "\n=== Test: Line Type Definitions ===" << std::endl;
+
+ const char* filename = "test_linetypes.dxf";
+
+ // Write line types
+ {
+ dxfRW dxf(filename);
+ class LineTypeWriter : public TestInterface {
+ public:
+ virtual void writeLTypes() {
+ // Continuous (standard)
+ DRW_LType continuous;
+ continuous.name = "CONTINUOUS";
+ continuous.desc = "Solid line";
+ dxfWriter->writeLineType(&continuous);
+
+ // Dashed
+ DRW_LType dashed;
+ dashed.name = "DASHED";
+ dashed.desc = "Dashed line";
+ dashed.path.push_back(0.5); // dash
+ dashed.path.push_back(-0.25); // gap
+ dxfWriter->writeLineType(&dashed);
+
+ // Dotted
+ DRW_LType dotted;
+ dotted.name = "DOTTED";
+ dotted.desc = "Dotted line";
+ dotted.path.push_back(0.0); // dot
+ dotted.path.push_back(-0.25); // gap
+ dxfWriter->writeLineType(&dotted);
+ }
+
+ virtual void writeEntities() {
+ // Line with dashed linetype
+ DRW_Line line;
+ line.basePoint.x = 0.0;
+ line.basePoint.y = 0.0;
+ line.basePoint.z = 0.0;
+ line.secPoint.x = 100.0;
+ line.secPoint.y = 0.0;
+ line.secPoint.z = 0.0;
+ line.lineType = "DASHED";
+ dxfWriter->writeLine(&line);
+ }
+
+ dxfRW* dxfWriter;
+ };
+
+ LineTypeWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write line types" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read line types" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.ltypeCount < 2) { // At least 2 line types
+ std::cout << "✗ Expected at least 2 line types, got " << reader.ltypeCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Line type definitions test passed (" << reader.ltypeCount << " line types)" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testLayersAndLineTypes() {
+ std::cout << "\n=== Test: Layers and Line Types Together ===" << std::endl;
+
+ const char* filename = "test_layers_linetypes.dxf";
+
+ // Write both layers and line types
+ {
+ dxfRW dxf(filename);
+ class LayerLineTypeWriter : public TestInterface {
+ public:
+ virtual void writeLayers() {
+ DRW_Layer layer1;
+ layer1.name = "Outline";
+ layer1.color = 1; // Red
+ layer1.lineType = "CONTINUOUS";
+ dxfWriter->writeLayer(&layer1);
+
+ DRW_Layer layer2;
+ layer2.name = "Centerline";
+ layer2.color = 2; // Yellow
+ layer2.lineType = "CENTER";
+ dxfWriter->writeLayer(&layer2);
+ }
+
+ virtual void writeLTypes() {
+ DRW_LType continuous;
+ continuous.name = "CONTINUOUS";
+ continuous.desc = "Solid";
+ dxfWriter->writeLineType(&continuous);
+
+ DRW_LType center;
+ center.name = "CENTER";
+ center.desc = "Center line";
+ center.path.push_back(1.25);
+ center.path.push_back(-0.25);
+ center.path.push_back(0.25);
+ center.path.push_back(-0.25);
+ dxfWriter->writeLineType(¢er);
+ }
+
+ virtual void writeEntities() {
+ // Line on Outline layer
+ DRW_Line line1;
+ line1.basePoint.x = 0.0;
+ line1.basePoint.y = 0.0;
+ line1.basePoint.z = 0.0;
+ line1.secPoint.x = 100.0;
+ line1.secPoint.y = 100.0;
+ line1.secPoint.z = 0.0;
+ line1.layer = "Outline";
+ line1.color = 256; // ByLayer
+ dxfWriter->writeLine(&line1);
+
+ // Line on Centerline layer
+ DRW_Line line2;
+ line2.basePoint.x = 50.0;
+ line2.basePoint.y = 0.0;
+ line2.basePoint.z = 0.0;
+ line2.secPoint.x = 50.0;
+ line2.secPoint.y = 100.0;
+ line2.secPoint.z = 0.0;
+ line2.layer = "Centerline";
+ line2.color = 256; // ByLayer
+ dxfWriter->writeLine(&line2);
+ }
+
+ dxfRW* dxfWriter;
+ };
+
+ LayerLineTypeWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write layers and line types" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read layers and line types" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.layerCount < 2 || reader.ltypeCount < 2 || reader.lineCount != 2) {
+ std::cout << "✗ Expected at least 2 layers, 2 line types, and 2 lines" << std::endl;
+ std::cout << " Got: " << reader.layerCount << " layers, "
+ << reader.ltypeCount << " line types, "
+ << reader.lineCount << " lines" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Layers and line types test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ std::cout << "libdxfrw Table Objects Tests" << std::endl;
+ std::cout << "=============================" << std::endl;
+
+ int failedTests = 0;
+ int totalTests = 0;
+
+ totalTests++;
+ if (!testLayerDefinitions()) failedTests++;
+
+ totalTests++;
+ if (!testLineTypes()) failedTests++;
+
+ totalTests++;
+ if (!testLayersAndLineTypes()) failedTests++;
+
+ std::cout << "\n=============================" << std::endl;
+ std::cout << "Tests: " << (totalTests - failedTests) << "/" << totalTests << " passed" << std::endl;
+
+ if (failedTests > 0) {
+ std::cout << "✗ " << failedTests << " test(s) failed" << std::endl;
+ return 1;
+ } else {
+ std::cout << "✓ All table object tests passed!" << std::endl;
+ return 0;
+ }
+}
diff --git a/tests/test_text.cpp b/tests/test_text.cpp
new file mode 100644
index 0000000..855312c
--- /dev/null
+++ b/tests/test_text.cpp
@@ -0,0 +1,287 @@
+/******************************************************************************
+** libDXFrw - Text Entity Tests **
+** **
+** Copyright (C) 2025 libdxfrw contributors **
+** **
+** This library is free software, licensed under the terms of the GNU **
+** General Public License as published by the Free Software Foundation, **
+** either version 2 of the License, or (at your option) any later version. **
+** You should have received a copy of the GNU General Public License **
+** along with this program. If not, see . **
+******************************************************************************/
+
+#include "libdxfrw.h"
+#include "test_interface.h"
+#include
+#include
+#include
+
+bool testSingleLineText() {
+ std::cout << "\n=== Test: Single Line Text ===" << std::endl;
+
+ const char* filename = "test_text.dxf";
+
+ // Write text
+ {
+ dxfRW dxf(filename);
+ class TextWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_Text text;
+ text.basePoint.x = 10.0;
+ text.basePoint.y = 20.0;
+ text.basePoint.z = 0.0;
+ text.text = "Hello, DXF World!";
+ text.height = 5.0;
+ text.angle = 0.0;
+ dxfWriter->writeText(&text);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ TextWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write text" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read text" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.textCount != 1) {
+ std::cout << "✗ Expected 1 text entity, got " << reader.textCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Single line text test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testMultiLineText() {
+ std::cout << "\n=== Test: Multi-Line Text (MText) ===" << std::endl;
+
+ const char* filename = "test_mtext.dxf";
+
+ // Write MText
+ {
+ dxfRW dxf(filename);
+ class MTextWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_MText mtext;
+ mtext.basePoint.x = 50.0;
+ mtext.basePoint.y = 50.0;
+ mtext.basePoint.z = 0.0;
+ mtext.text = "Line 1\\PLine 2\\PLine 3"; // \\P is paragraph break
+ mtext.height = 3.0;
+ mtext.widthscale = 1.0;
+ dxfWriter->writeMText(&mtext);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ MTextWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write MText" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read MText" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.mtextCount != 1) {
+ std::cout << "✗ Expected 1 MText entity, got " << reader.mtextCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Multi-line text test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testRotatedText() {
+ std::cout << "\n=== Test: Rotated Text ===" << std::endl;
+
+ const char* filename = "test_rotated_text.dxf";
+
+ // Write rotated text
+ {
+ dxfRW dxf(filename);
+ class RotatedTextWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ // Text at 0 degrees
+ DRW_Text text1;
+ text1.basePoint.x = 0.0;
+ text1.basePoint.y = 0.0;
+ text1.basePoint.z = 0.0;
+ text1.text = "0 degrees";
+ text1.height = 5.0;
+ text1.angle = 0.0;
+ dxfWriter->writeText(&text1);
+
+ // Text at 45 degrees
+ DRW_Text text2;
+ text2.basePoint.x = 0.0;
+ text2.basePoint.y = 20.0;
+ text2.basePoint.z = 0.0;
+ text2.text = "45 degrees";
+ text2.height = 5.0;
+ text2.angle = M_PI / 4.0; // 45 degrees in radians
+ dxfWriter->writeText(&text2);
+
+ // Text at 90 degrees
+ DRW_Text text3;
+ text3.basePoint.x = 0.0;
+ text3.basePoint.y = 40.0;
+ text3.basePoint.z = 0.0;
+ text3.text = "90 degrees";
+ text3.height = 5.0;
+ text3.angle = M_PI / 2.0; // 90 degrees in radians
+ dxfWriter->writeText(&text3);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ RotatedTextWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write rotated text" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read rotated text" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.textCount != 3) {
+ std::cout << "✗ Expected 3 text entities, got " << reader.textCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Rotated text test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+bool testTextWithDifferentHeights() {
+ std::cout << "\n=== Test: Text with Different Heights ===" << std::endl;
+
+ const char* filename = "test_text_heights.dxf";
+
+ // Write text with different heights
+ {
+ dxfRW dxf(filename);
+ class TextHeightWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ for (int i = 1; i <= 5; i++) {
+ DRW_Text text;
+ text.basePoint.x = 0.0;
+ text.basePoint.y = i * 15.0;
+ text.basePoint.z = 0.0;
+ text.text = "Height " + std::to_string(i * 2);
+ text.height = i * 2.0;
+ text.angle = 0.0;
+ dxfWriter->writeText(&text);
+ }
+ }
+ dxfRW* dxfWriter;
+ };
+
+ TextHeightWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, false)) {
+ std::cout << "✗ Failed to write text with different heights" << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename);
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read text with different heights" << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ if (reader.textCount != 5) {
+ std::cout << "✗ Expected 5 text entities, got " << reader.textCount << std::endl;
+ std::remove(filename);
+ return false;
+ }
+
+ std::cout << "✓ Text with different heights test passed" << std::endl;
+ }
+
+ std::remove(filename);
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ std::cout << "libdxfrw Text Entity Tests" << std::endl;
+ std::cout << "==========================" << std::endl;
+
+ int failedTests = 0;
+ int totalTests = 0;
+
+ totalTests++;
+ if (!testSingleLineText()) failedTests++;
+
+ totalTests++;
+ if (!testMultiLineText()) failedTests++;
+
+ totalTests++;
+ if (!testRotatedText()) failedTests++;
+
+ totalTests++;
+ if (!testTextWithDifferentHeights()) failedTests++;
+
+ std::cout << "\n==========================" << std::endl;
+ std::cout << "Tests: " << (totalTests - failedTests) << "/" << totalTests << " passed" << std::endl;
+
+ if (failedTests > 0) {
+ std::cout << "✗ " << failedTests << " test(s) failed" << std::endl;
+ return 1;
+ } else {
+ std::cout << "✓ All text entity tests passed!" << std::endl;
+ return 0;
+ }
+}
diff --git a/tests/test_versions.cpp b/tests/test_versions.cpp
new file mode 100644
index 0000000..4cde694
--- /dev/null
+++ b/tests/test_versions.cpp
@@ -0,0 +1,190 @@
+/******************************************************************************
+** libDXFrw - DXF Version Tests **
+** **
+** Copyright (C) 2025 libdxfrw contributors **
+** **
+** This library is free software, licensed under the terms of the GNU **
+** General Public License as published by the Free Software Foundation, **
+** either version 2 of the License, or (at your option) any later version. **
+** You should have received a copy of the GNU General Public License **
+** along with this program. If not, see . **
+******************************************************************************/
+
+#include "libdxfrw.h"
+#include "test_interface.h"
+#include
+#include
+
+bool testDXFVersion(DRW::Version version, const char* versionName) {
+ std::cout << "\n=== Test: DXF Version " << versionName << " ===" << std::endl;
+
+ std::string filename = std::string("test_") + versionName + ".dxf";
+
+ // Write file in specific version
+ {
+ dxfRW dxf(filename.c_str());
+ class VersionWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ // Write various entities
+ DRW_Point point;
+ point.basePoint.x = 10.0;
+ point.basePoint.y = 20.0;
+ point.basePoint.z = 0.0;
+ dxfWriter->writePoint(&point);
+
+ DRW_Line line;
+ line.basePoint.x = 0.0;
+ line.basePoint.y = 0.0;
+ line.basePoint.z = 0.0;
+ line.secPoint.x = 100.0;
+ line.secPoint.y = 100.0;
+ line.secPoint.z = 0.0;
+ dxfWriter->writeLine(&line);
+
+ DRW_Circle circle;
+ circle.basePoint.x = 50.0;
+ circle.basePoint.y = 50.0;
+ circle.basePoint.z = 0.0;
+ circle.radious = 25.0;
+ dxfWriter->writeCircle(&circle);
+
+ DRW_Arc arc;
+ arc.basePoint.x = 100.0;
+ arc.basePoint.y = 100.0;
+ arc.basePoint.z = 0.0;
+ arc.radious = 30.0;
+ arc.staangle = 0.0;
+ arc.endangle = M_PI / 2.0;
+ dxfWriter->writeArc(&arc);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ VersionWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, version, false)) {
+ std::cout << "✗ Failed to write DXF " << versionName << std::endl;
+ return false;
+ }
+ }
+
+ // Read and verify
+ {
+ dxfRW dxf(filename.c_str());
+ TestInterface reader;
+ if (!dxf.read(&reader, false)) {
+ std::cout << "✗ Failed to read DXF " << versionName << std::endl;
+ std::remove(filename.c_str());
+ return false;
+ }
+
+ bool valid = (reader.pointCount == 1 && reader.lineCount == 1 &&
+ reader.circleCount == 1 && reader.arcCount == 1);
+
+ if (!valid) {
+ std::cout << "✗ Entity counts don't match for " << versionName << std::endl;
+ std::cout << " Expected: 1 point, 1 line, 1 circle, 1 arc" << std::endl;
+ std::cout << " Got: " << reader.pointCount << " points, "
+ << reader.lineCount << " lines, "
+ << reader.circleCount << " circles, "
+ << reader.arcCount << " arcs" << std::endl;
+ std::remove(filename.c_str());
+ return false;
+ }
+
+ std::cout << "✓ DXF " << versionName << " test passed" << std::endl;
+ }
+
+ std::remove(filename.c_str());
+ return true;
+}
+
+bool testBinaryDXFWrite() {
+ std::cout << "\n=== Test: Binary DXF Write ===" << std::endl;
+
+ const char* binaryFile = "test_binary_write.dxf";
+
+ // Test that we can write a binary DXF file
+ {
+ dxfRW dxf(binaryFile);
+ class BinaryWriter : public TestInterface {
+ public:
+ virtual void writeEntities() {
+ DRW_Line line;
+ line.basePoint.x = 0.0;
+ line.basePoint.y = 0.0;
+ line.basePoint.z = 0.0;
+ line.secPoint.x = 100.0;
+ line.secPoint.y = 100.0;
+ line.secPoint.z = 0.0;
+ dxfWriter->writeLine(&line);
+
+ DRW_Circle circle;
+ circle.basePoint.x = 50.0;
+ circle.basePoint.y = 50.0;
+ circle.basePoint.z = 0.0;
+ circle.radious = 25.0;
+ dxfWriter->writeCircle(&circle);
+ }
+ dxfRW* dxfWriter;
+ };
+
+ BinaryWriter writer;
+ writer.dxfWriter = &dxf;
+ if (!dxf.write(&writer, DRW::AC1015, true)) { // Binary
+ std::cout << "✗ Failed to write binary DXF" << std::endl;
+ return false;
+ }
+ }
+
+ // Note: Binary DXF reading may have limitations in this library
+ // We just verify the file was written
+ std::cout << "✓ Binary DXF write test passed" << std::endl;
+
+ std::remove(binaryFile);
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ std::cout << "libdxfrw DXF Version Tests" << std::endl;
+ std::cout << "==========================" << std::endl;
+
+ int failedTests = 0;
+ int totalTests = 0;
+
+ // Test R12
+ totalTests++;
+ if (!testDXFVersion(DRW::AC1009, "R12")) failedTests++;
+
+ // Test R2000
+ totalTests++;
+ if (!testDXFVersion(DRW::AC1015, "R2000")) failedTests++;
+
+ // Test R2004
+ totalTests++;
+ if (!testDXFVersion(DRW::AC1018, "R2004")) failedTests++;
+
+ // Test R2007
+ totalTests++;
+ if (!testDXFVersion(DRW::AC1021, "R2007")) failedTests++;
+
+ // Test R2010
+ totalTests++;
+ if (!testDXFVersion(DRW::AC1024, "R2010")) failedTests++;
+
+ // Test binary DXF write
+ totalTests++;
+ if (!testBinaryDXFWrite()) failedTests++;
+
+ std::cout << "\n==========================" << std::endl;
+ std::cout << "Tests: " << (totalTests - failedTests) << "/" << totalTests << " passed" << std::endl;
+
+ if (failedTests > 0) {
+ std::cout << "✗ " << failedTests << " test(s) failed" << std::endl;
+ return 1;
+ } else {
+ std::cout << "✓ All version tests passed!" << std::endl;
+ return 0;
+ }
+}