1/* 2 * Copyright 2008-2009, 2023, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Fran��ois Revol <revol@free.fr> 7 * Zardshard 8 */ 9 10#include "StyledTextImporter.h" 11 12#include <new> 13#include <stdint.h> 14#include <stdio.h> 15 16#include <Alert.h> 17#include <Archivable.h> 18#include <ByteOrder.h> 19#include <Catalog.h> 20#include <DataIO.h> 21#include <File.h> 22#include <List.h> 23#include <Locale.h> 24#include <Message.h> 25#include <NodeInfo.h> 26#include <Shape.h> 27#include <String.h> 28#include <TextView.h> 29 30#include "Defines.h" 31#include "Icon.h" 32#include "PathSourceShape.h" 33#include "Style.h" 34#include "VectorPath.h" 35 36 37#undef B_TRANSLATION_CONTEXT 38#define B_TRANSLATION_CONTEXT "Icon-O-Matic-StyledTextImport" 39//#define CALLED() printf("%s()\n", __FUNCTION__); 40#define CALLED() do {} while (0) 41 42 43using std::nothrow; 44 45class ShapeIterator : public BShapeIterator { 46 public: 47 ShapeIterator(Icon *icon, PathSourceShape *to, BPoint offset, const char *name); 48 ~ShapeIterator() {}; 49 50 virtual status_t IterateMoveTo(BPoint *point); 51 virtual status_t IterateLineTo(int32 lineCount, BPoint *linePts); 52 virtual status_t IterateBezierTo(int32 bezierCount, BPoint *bezierPts); 53 virtual status_t IterateClose(); 54 55 private: 56 VectorPath *CurrentPath(); 57 void NextPath(); 58 59 Icon *fIcon; 60 PathSourceShape *fShape; 61 VectorPath *fPath; 62 BPoint fOffset; 63 const char *fName; 64 BPoint fLastPoint; 65 bool fHasLastPoint; 66}; 67 68ShapeIterator::ShapeIterator(Icon *icon, PathSourceShape *to, BPoint offset, 69 const char *name) 70{ 71 CALLED(); 72 fIcon = icon; 73 fShape = to; 74 fPath = NULL; 75 fOffset = offset; 76 fName = name; 77 fLastPoint = offset; 78 fHasLastPoint = false; 79} 80 81status_t 82ShapeIterator::IterateMoveTo(BPoint *point) 83{ 84 CALLED(); 85 if (fPath) 86 NextPath(); 87 if (!CurrentPath()) 88 return B_ERROR; 89 //fPath->AddPoint(fOffset + *point); 90 fLastPoint = fOffset + *point; 91 fHasLastPoint = true; 92 return B_OK; 93} 94 95status_t 96ShapeIterator::IterateLineTo(int32 lineCount, BPoint *linePts) 97{ 98 CALLED(); 99 if (!CurrentPath()) 100 return B_ERROR; 101 while (lineCount--) { 102 fPath->AddPoint(fOffset + *linePts); 103 fLastPoint = fOffset + *linePts; 104 fHasLastPoint = true; 105 linePts++; 106 } 107 return B_OK; 108} 109 110status_t 111ShapeIterator::IterateBezierTo(int32 bezierCount, BPoint *bezierPts) 112{ 113 CALLED(); 114 if (!CurrentPath()) 115 return B_ERROR; 116 117 BPoint firstPoint(fLastPoint); 118 fLastPoint = fOffset + bezierPts[bezierCount * 3 - 1]; 119 120 // first point 121 if (firstPoint == fLastPoint) { 122 // combine the first and the last point 123 fPath->AddPoint(firstPoint, 124 fOffset + bezierPts[bezierCount * 3 - 2], fOffset + bezierPts[0], false); 125 // Mark the points as disconnected for now. CleanUp will change this if necessary. 126 fPath->SetClosed(true); 127 } else { 128 fPath->AddPoint(firstPoint, firstPoint, fOffset + bezierPts[0], false); 129 } 130 131 // middle points 132 for (int i = 1; i + 2 < bezierCount * 3; i += 3) { 133 fPath->AddPoint(fOffset + bezierPts[i + 1], 134 fOffset + bezierPts[i + 0], fOffset + bezierPts[i + 2], false); 135 } 136 137 // last point 138 if (firstPoint != fLastPoint) { 139 fPath->AddPoint(fLastPoint, 140 fOffset + bezierPts[bezierCount * 3 - 2], fLastPoint, false); 141 } 142 143 fHasLastPoint = true; 144 return B_OK; 145} 146 147status_t 148ShapeIterator::IterateClose() 149{ 150 CALLED(); 151 if (!CurrentPath()) 152 return B_ERROR; 153 fPath->SetClosed(true); 154 NextPath(); 155 fHasLastPoint = false; 156 return B_OK; 157} 158 159VectorPath * 160ShapeIterator::CurrentPath() 161{ 162 CALLED(); 163 if (fPath) 164 return fPath; 165 fPath = new (nothrow) VectorPath(); 166 fPath->SetName(fName); 167 return fPath; 168} 169 170void 171ShapeIterator::NextPath() 172{ 173 CALLED(); 174 if (fPath) { 175 fPath->CleanUp(); 176 fIcon->Paths()->AddItem(fPath); 177 fShape->Paths()->AddItem(fPath); 178 } 179 fPath = NULL; 180} 181 182 183// #pragma mark - 184 185// constructor 186StyledTextImporter::StyledTextImporter() 187 : Importer(), 188 fStyleMap(NULL), 189 fStyleCount(0) 190{ 191 CALLED(); 192} 193 194// destructor 195StyledTextImporter::~StyledTextImporter() 196{ 197 CALLED(); 198} 199 200// Import 201status_t 202StyledTextImporter::Import(Icon* icon, BMessage* clipping) 203{ 204 CALLED(); 205 const char *text; 206 ssize_t textLength; 207 208 if (clipping == NULL) 209 return ENOENT; 210 if (clipping->FindData("text/plain", 211 B_MIME_TYPE, (const void **)&text, &textLength) == B_OK) { 212 text_run_array *runs = NULL; 213 ssize_t runsLength; 214 if (clipping->FindData("application/x-vnd.Be-text_run_array", 215 B_MIME_TYPE, (const void **)&runs, &runsLength) < B_OK) 216 runs = NULL; 217 BString str(text, textLength); 218 return _Import(icon, str.String(), runs); 219 } 220 return ENOENT; 221} 222 223// Import 224status_t 225StyledTextImporter::Import(Icon* icon, const entry_ref* ref) 226{ 227 CALLED(); 228 status_t err; 229 BFile file(ref, B_READ_ONLY); 230 err = file.InitCheck(); 231 if (err < B_OK) 232 return err; 233 234 BNodeInfo info(&file); 235 char mime[B_MIME_TYPE_LENGTH]; 236 err = info.GetType(mime); 237 if (err < B_OK) 238 return err; 239 240 if (strncmp(mime, "text/plain", B_MIME_TYPE_LENGTH)) 241 return EINVAL; 242 243 off_t size; 244 err = file.GetSize(&size); 245 if (err < B_OK) 246 return err; 247 if (size > 1 * 1024 * 1024) // Don't load files that big 248 return E2BIG; 249 250 BMallocIO mio; 251 mio.SetSize((size_t)size + 1); 252 memset((void *)mio.Buffer(), 0, (size_t)size + 1); 253 254 // TODO: read runs from attribute 255 256 return _Import(icon, (const char *)mio.Buffer(), NULL); 257} 258 259 260// _Import 261status_t 262StyledTextImporter::_Import(Icon* icon, const char *text, text_run_array *runs) 263{ 264 CALLED(); 265 status_t ret = Init(icon); 266 if (ret < B_OK) { 267 printf("StyledTextImporter::Import() - Init() error: %s\n", 268 strerror(ret)); 269 return ret; 270 } 271 272 BString str(text); 273 if (str.Length() > 50) { 274 BAlert* alert = new BAlert(B_TRANSLATE("Text too long"), 275 B_TRANSLATE("The text you are trying to import is quite long, " 276 "are you sure?"), 277 B_TRANSLATE("Import text"), B_TRANSLATE("Cancel"), NULL, 278 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 279 if (alert->Go()) 280 return B_CANCELED; 281 } 282 283 // import run colors as styles 284 if (runs) { 285 delete[] fStyleMap; 286 fStyleMap = new struct style_map[runs->count]; 287 for (int32 i = 0; runs && i < runs->count; i++) { 288 _AddStyle(icon, &runs->runs[i]); 289 } 290 } 291 292 int32 currentRun = 0; 293 text_run *run = NULL; 294 if (runs) 295 run = &runs->runs[0]; 296 int32 len = str.Length(); 297 int32 chars = str.CountChars(); 298 BPoint origin(0, 0); 299 BPoint offset(origin); 300 301 for (int32 i = 0, c = 0; i < len && c < chars; c++) { 302 // make sure we are still on the (good) run 303 while (run && currentRun < runs->count - 1 && 304 i >= runs->runs[currentRun + 1].offset) { 305 run = &runs->runs[++currentRun]; 306 //printf("switching to run %d\n", currentRun); 307 } 308 309 int charLen; 310 for (charLen = 1; str.ByteAt(i + charLen) & 0x80; charLen++); 311 312 BShape glyph; 313 BShape *glyphs[1] = { &glyph }; 314 BFont font(be_plain_font); 315 if (run) 316 font = run->font; 317 318 // first char 319 if (offset == BPoint(0,0)) { 320 font_height height; 321 font.GetHeight(&height); 322 origin.y += height.ascent; 323 offset = origin; 324 } 325 // LF 326 if (str[i] == '\n') { 327 // XXX: should take the MAX() for the line 328 // XXX: should use descent + leading from previous line 329 font_height height; 330 font.GetHeight(&height); 331 origin.y += height.ascent + height.descent + height.leading; 332 offset = origin; 333 i++; 334 continue; 335 } 336 337 float charWidth; 338 charWidth = font.StringWidth(str.String() + i, charLen); 339 //printf("StringWidth( %d) = %f\n", charLen, charWidth); 340 BString glyphName(str.String() + i, charLen); 341 glyphName.Prepend("Glyph ("); 342 glyphName.Append(")"); 343 344 font.GetGlyphShapes((str.String() + i), 1, glyphs); 345 if (glyph.Bounds().IsValid()) { 346 //offset.x += glyph.Bounds().Width(); 347 offset.x += charWidth; 348 PathSourceShape* shape = new (nothrow) PathSourceShape(NULL); 349 if (shape == NULL) 350 return B_NO_MEMORY; 351 shape->SetName(glyphName.String()); 352 if (!icon->Shapes()->AddItem(shape)) { 353 delete shape; 354 return B_NO_MEMORY; 355 } 356 for (int j = 0; run && j < fStyleCount; j++) { 357 if (fStyleMap[j].run == run) { 358 shape->SetStyle(fStyleMap[j].style); 359 break; 360 } 361 } 362 ShapeIterator iterator(icon, shape, offset, glyphName.String()); 363 if (iterator.Iterate(&glyph) < B_OK) 364 return B_ERROR; 365 366 } 367 368 // skip the rest of UTF-8 char bytes 369 for (i++; i < len && str[i] & 0x80; i++); 370 } 371 372 delete[] fStyleMap; 373 fStyleMap = NULL; 374 375 return B_OK; 376} 377 378// #pragma mark - 379 380// _AddStyle 381status_t 382StyledTextImporter::_AddStyle(Icon *icon, text_run *run) 383{ 384 CALLED(); 385 if (!run) 386 return EINVAL; 387 rgb_color color = run->color; 388 Style* style = new(std::nothrow) Style(color); 389 if (style == NULL) 390 return B_NO_MEMORY; 391 char name[30]; 392 sprintf(name, B_TRANSLATE_COMMENT("Color (#%02x%02x%02x)", 393 "Style name after dropping a color"), 394 color.red, color.green, color.blue); 395 style->SetName(name); 396 397 bool found = false; 398 for (int i = 0; i < fStyleCount; i++) { 399 if (*style == *(fStyleMap[i].style)) { 400 delete style; 401 style = fStyleMap[i].style; 402 found = true; 403 break; 404 } 405 } 406 407 if (!found && !icon->Styles()->AddItem(style)) { 408 delete style; 409 return B_NO_MEMORY; 410 } 411 412 fStyleMap[fStyleCount].run = run; 413 fStyleMap[fStyleCount].style = style; 414 fStyleCount++; 415 416 return B_OK; 417} 418 419// _AddPaths 420status_t 421StyledTextImporter::_AddPaths(Icon *icon, BShape *shape) 422{ 423 CALLED(); 424 return B_ERROR; 425} 426 427// _AddShape 428status_t 429StyledTextImporter::_AddShape(Icon *icon, BShape *shape, text_run *run) 430{ 431 CALLED(); 432 return B_ERROR; 433} 434