Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ debug
compile
.cproject
.project
.serena
CMakeCache.txt
cmake_install.cmake
src/intern/.dirstamp
Expand Down
5 changes: 4 additions & 1 deletion bin/dwg2txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,15 @@ if [ ! -f "$INPUT_FILE" ]; then
fi

# Check if dwg2text exists
DWG2TEXT="${SCRIPT_DIR}/dwg2text"
DWG2TEXT="${SCRIPT_DIR}/../dwg2text/.libs/dwg2text"

@rurban rurban Jan 22, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong. The libtool wrapper is there and provides a proper LD search path fix up already. Just portable. This is not

if [ ! -x "$DWG2TEXT" ]; then
echo "Error: dwg2text utility not found or not executable: $DWG2TEXT"
exit 1
fi

# Set library path for shared libraries
export LD_LIBRARY_PATH="${SCRIPT_DIR}/../src/.libs:$LD_LIBRARY_PATH"

# Convert DWG to text
if "$DWG2TEXT" "$INPUT_FILE" > "$OUTPUT_FILE" 2>&1; then
echo "✓ Successfully converted: $OUTPUT_FILE"
Expand Down
162 changes: 162 additions & 0 deletions dwg2text/dx_iface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,150 @@

#include <iostream>
#include <algorithm>
#include <regex>
#include "dx_iface.h"
#include "libdwgr.h"
#include "libdxfrw.h"

/**
* Strip MTEXT formatting codes and return plain text.
* MTEXT uses various formatting codes like:
* \P - paragraph break (newline)
* \\ - literal backslash
* \{ - literal opening brace
* \} - literal closing brace
* {\fFont|...;text} - font specification
* \L, \l - underline on/off
* \O, \o - overline on/off
* \K, \k - strikethrough on/off
* \S...^...; - stacked fraction
* \H...x; - height change
* \W...x; - width change
* \Q...; - oblique angle
* \T...; - tracking
* \A...; - alignment
* \C...; - color
* { } - grouping (removed)
*/
static std::string stripMTextFormatting(const std::string& text) {
std::string result;
result.reserve(text.size());

size_t i = 0;
while (i < text.size()) {
// Check for escape character (backslash or yen symbol)
// Yen symbol in UTF-8 is 0xC2 0xA5
bool isEscape = false;
size_t escapeLen = 0;

if (text[i] == '\\') {
isEscape = true;
escapeLen = 1;
} else if (i + 1 < text.size() &&
static_cast<unsigned char>(text[i]) == 0xC2 &&
static_cast<unsigned char>(text[i + 1]) == 0xA5) {
// UTF-8 encoded yen symbol (¥)
isEscape = true;
escapeLen = 2;
}

if (isEscape && i + escapeLen < text.size()) {
char next = text[i + escapeLen];
switch (next) {
case 'P':
case 'p':
// Paragraph break -> newline
result += '\n';
i += escapeLen + 1;
break;
case '\\':
// Escaped backslash
result += '\\';
i += escapeLen + 1;
break;
case '{':
result += '{';
i += escapeLen + 1;
break;
case '}':
result += '}';
i += escapeLen + 1;
break;
case 'L':
case 'l':
case 'O':
case 'o':
case 'K':
case 'k':
// Underline/overline/strikethrough toggles - skip
i += escapeLen + 1;
break;
case 'S': {
// Stacked fraction: \Snum^denom; or \Snum/denom; or \Snum#denom;
i += escapeLen + 1;
std::string num, denom;
while (i < text.size() && text[i] != '^' && text[i] != '/' && text[i] != '#' && text[i] != ';') {
num += text[i++];
}
if (i < text.size() && (text[i] == '^' || text[i] == '/' || text[i] == '#')) {
i++; // skip separator
while (i < text.size() && text[i] != ';') {
denom += text[i++];
}
}
if (i < text.size() && text[i] == ';') i++; // skip semicolon
if (!num.empty() && !denom.empty()) {
result += num + "/" + denom;
} else {
result += num + denom;
}
break;
}
case 'H':
case 'W':
case 'Q':
case 'T':
case 'A':
case 'C':
case 'h':
case 'w':
case 'q':
case 't':
case 'a':
case 'c':
// Format codes with value: \Xvalue; - skip to semicolon
i += escapeLen + 1;
while (i < text.size() && text[i] != ';') {
i++;
}
if (i < text.size() && text[i] == ';') i++;
break;
case 'F':
case 'f':
// Font specification: \fFontName|options; - skip to semicolon
i += escapeLen + 1;
while (i < text.size() && text[i] != ';') {
i++;
}
if (i < text.size() && text[i] == ';') i++;
break;
default:
// Unknown escape - keep character as is
result += text[i];
i++;
break;
}
} else if (text[i] == '{' || text[i] == '}') {
// Skip grouping braces
i++;
} else {
result += text[i];
i++;
}
}

return result;
}

bool dx_iface::printText(const std::string& fileI, dx_data *fData){
unsigned int found = fileI.find_last_of(".");
Expand Down Expand Up @@ -46,6 +186,28 @@ bool dx_iface::printText(const std::string& fileI, dx_data *fData){
case DRW::TEXT:
std::cout << static_cast<DRW_Text*>(e)->text << std::endl;
break;
case DRW::MTEXT: {
std::string mtextContent = static_cast<DRW_MText*>(e)->text;
std::string plainText = stripMTextFormatting(mtextContent);
if (!plainText.empty()) {
std::cout << plainText << std::endl;
}
break;
}
case DRW::DIMLINEAR:
case DRW::DIMALIGNED:
case DRW::DIMANGULAR:
case DRW::DIMANGULAR3P:
case DRW::DIMRADIAL:
case DRW::DIMDIAMETRIC:
case DRW::DIMORDINATE: {
DRW_Dimension* dim = static_cast<DRW_Dimension*>(e);
std::string dimText = dim->getText();
if (!dimText.empty()) {
std::cout << dimText << std::endl;
}
break;
}
default:
break;
}
Expand Down
Loading