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; + } +}