Browse Source

初版调试完成

master
hejl 1 month ago
parent
commit
13a0f5d618
  1. 1
      gofaster/.vscode/settings.json
  2. 125
      win_text_editor/lib/modules/pdf_parse/services/excel_data_adjustment_service.dart
  3. 316
      win_text_editor/lib/modules/pdf_parse/services/excel_deal_service.dart
  4. 169
      win_text_editor/lib/modules/pdf_parse/services/relationship_calculator_service.dart

1
gofaster/.vscode/settings.json vendored

@ -0,0 +1 @@ @@ -0,0 +1 @@
{}

125
win_text_editor/lib/modules/pdf_parse/services/excel_data_adjustment_service.dart

@ -0,0 +1,125 @@ @@ -0,0 +1,125 @@
import 'package:excel/excel.dart';
class ExcelDataAdjustmentService {
static const Map<String, int> palaceDegreeAdjustments = {
'': 300,
'': 270,
'': 240,
'': 210,
'': 180,
'': 150,
'': 120,
'': 90,
'': 60,
'': 30,
'': 0,
'': 330,
};
static final _degreeFormatRegex = RegExp(
r"^(\d+)\s*([^\d\s°']+)\s*(\d+)\s*(?:['′]\s*(\d{1,2})\s*)?$",
caseSensitive: false,
);
//
List<String?> processRowData(
List<String> cells,
Map<int, String> previousPalaces,
Sheet sheet,
int rowIndex,
int startCol, //
) {
List<String?> currentRowPalaces = List.filled(14, null);
int outputCol = startCol; // 使
for (int col = 4; col <= 17 && col < cells.length; col++) {
final cellValue = cells[col];
//
final palace = _extractPalace(cellValue);
String palaceToWrite = palace;
// R/D使
if (palaceToWrite.isEmpty || palaceToWrite == 'R' || palaceToWrite == 'D') {
palaceToWrite = previousPalaces[col] ?? '';
} else {
//
previousPalaces[col] = palaceToWrite;
}
currentRowPalaces[col - 4] = palaceToWrite;
//
sheet.cell(CellIndex.indexByColumnRow(
columnIndex: outputCol,
rowIndex: rowIndex
)).value = TextCellValue(palaceToWrite);
outputCol++;
//
final displayValue = _getFormattedValueWithAdjustment(cellValue, palaceToWrite);
sheet.cell(CellIndex.indexByColumnRow(
columnIndex: outputCol,
rowIndex: rowIndex
)).value = TextCellValue(displayValue);
outputCol++;
}
return currentRowPalaces;
}
String _getFormattedValueWithAdjustment(String originalValue, String palace) {
// 1.
String normalized = originalValue.replaceAll('', "'").replaceAll(' ', '').replaceAll(' ', '');
// 2.
String withDegreeSymbol = _replaceZodiacWithDegreeSymbol(normalized);
// 3.
if (palace.isNotEmpty && palaceDegreeAdjustments.containsKey(palace)) {
return _adjustDegreeByPalace(withDegreeSymbol, palace);
}
return withDegreeSymbol;
}
String _replaceZodiacWithDegreeSymbol(String value) {
final match = _degreeFormatRegex.firstMatch(value);
if (match == null) return value;
final degreeStr = match.group(1);
final minuteStr = match.group(3);
final secondStr = match.group(4);
if (degreeStr == null || minuteStr == null) return value;
return '${degreeStr}°${minuteStr.padLeft(2, '0')}'
'${secondStr != null ? "'${secondStr.padLeft(2, '0')}\"" : ""}';
}
String _adjustDegreeByPalace(String valueWithDegree, String palace) {
final match = RegExp(r"^(\d+)°(\d+)(?:['](\d{1,2}))?").firstMatch(valueWithDegree);
if (match == null) return valueWithDegree;
final degreeStr = match.group(1);
final minuteStr = match.group(2);
final secondStr = match.group(3);
if (degreeStr == null || minuteStr == null) return valueWithDegree;
final originalDegree = int.tryParse(degreeStr) ?? 0;
final adjustment = palaceDegreeAdjustments[palace] ?? 0;
var adjustedDegree = originalDegree + adjustment;
adjustedDegree %= 360;
if (adjustedDegree < 0) adjustedDegree += 360;
return '${adjustedDegree}°${minuteStr.padLeft(2, '0')}'
'${secondStr != null ? "'${secondStr.padLeft(2, '0')}\"" : ""}';
}
String _extractPalace(String cellValue) {
final match = RegExp(r"[^\d\s°\'RDL]").firstMatch(cellValue.trim());
return match?.group(0) ?? '';
}
}

316
win_text_editor/lib/modules/pdf_parse/services/excel_deal_service.dart

@ -1,11 +1,15 @@ @@ -1,11 +1,15 @@
import 'dart:io';
import 'package:excel/excel.dart';
import 'package:file_picker/file_picker.dart';
import 'package:win_text_editor/app/providers/logger.dart';
import 'excel_data_adjustment_service.dart';
import 'relationship_calculator_service.dart';
class ExcelDealService {
static const String _logTag = 'ExcelDealService';
final ExcelDataAdjustmentService _adjustmentService = ExcelDataAdjustmentService();
final RelationshipCalculatorService _relationshipService = RelationshipCalculatorService();
static const Map<int, String> _columnTitles = {
4: '',
5: '',
@ -23,291 +27,109 @@ class ExcelDealService { @@ -23,291 +27,109 @@ class ExcelDealService {
17: '',
};
static const List<Set<String>> harmonyGroups = [
{'', '', ''}, //
{'', '', ''}, //
{'', '', ''}, //
{'', '', ''}, //
];
static const List<Set<String>> conflictGroups = [
{'', ''}, //
{'', ''}, //
{'', ''}, //
{'', ''}, //
{'', ''}, //
{'', ''}, //
];
static const List<Set<String>> punishmentGroups = [
{'', ''}, //
{'', ''}, //
{'', ''}, //
{'', ''}, //
{'', ''}, //
{'', ''}, //
{'', ''}, //
{'', '', ''}, //
{'', '', ''}, //
];
Future<void> exportToExcel(String processedContent, {String? fileName}) async {
try {
final excel = Excel.createExcel();
final sheet = excel['Sheet1'];
final lines = processedContent.split('\n');
bool isHeaderProcessed = false;
final Map<int, String> previousPalaces = {};
// 1.
for (int rowIndex = 0; rowIndex < lines.length; rowIndex++) {
final line = lines[rowIndex];
if (line.trim().isEmpty) continue;
final cells = line.split(',');
//
if (cells.isNotEmpty && cells[0] == 'Date') {
//
final extendedCells = [...cells, '', '', ''];
//
for (int col = 0; col < extendedCells.length; col++) {
final cell = sheet.cell(
CellIndex.indexByColumnRow(columnIndex: col, rowIndex: rowIndex),
);
cell.value = TextCellValue(extendedCells[col]);
// 绿
cell.cellStyle = CellStyle(
backgroundColorHex: ExcelColor.lightGreen,
fontColorHex: ExcelColor.black,
bold: true,
);
}
isHeaderProcessed = true;
_processHeaderRow(sheet, rowIndex, cells);
continue;
}
//
for (int col = 0; col < cells.length; col++) {
final cellValue = cells[col];
final cell = sheet.cell(CellIndex.indexByColumnRow(columnIndex: col, rowIndex: rowIndex));
cell.value = TextCellValue(cellValue);
// 7-14(6-13)
if (col >= 6 && col <= 13) {
_applyConditionalFormatting(cell, cellValue);
}
}
//
if (isHeaderProcessed && rowIndex > 0) {
_calculateRelationships(sheet, rowIndex, cells);
int outputCol = 0;
// 1.1
for (int col = 0; col < 4 && col < cells.length; col++) {
sheet.cell(CellIndex.indexByColumnRow(
columnIndex: outputCol,
rowIndex: rowIndex
)).value = TextCellValue(cells[col]);
outputCol++;
}
}
//
for (var i = 1; i < 18; i++) {
sheet.setColumnWidth(i, 8.0);
}
final bytes = excel.save()!;
//
final String? outputPath = await FilePicker.platform.saveFile(
dialogTitle: 'Save Excel File',
fileName: fileName ?? 'pdf_export_${DateTime.now().millisecondsSinceEpoch}.xlsx',
allowedExtensions: ['xlsx'],
type: FileType.custom,
);
// 1.2 宿
_adjustmentService.processRowData(
cells,
previousPalaces,
sheet,
rowIndex,
outputCol
);
}
if (outputPath == null) {
Logger().info('User cancelled Excel export');
return;
}
// 2. sheet
_relationshipService.processEntireSheet(sheet);
await File(outputPath).writeAsBytes(bytes);
Logger().info('文件已导出至: $outputPath');
_setColumnWidths(sheet);
await _saveExcelFile(excel, fileName);
} catch (e) {
Logger().error('[$_logTag] Excel export失败: ${e.toString()}');
rethrow;
}
}
void _calculateRelationships(Sheet sheet, int rowIndex, List<String> cells) {
final List<CellData> validCells = [];
// 1. Extract valid cells (columns 5-18, excluding 15-16) with degree, zodiac and minute
for (int col = 4; col <= 17; col++) {
if (col == 14 || col == 15) continue; // Skip South/North columns
void _processHeaderRow(Sheet sheet, int rowIndex, List<String> cells) {
final extendedCells = <String>[];
if (col < cells.length) {
final data = _parseCellData(cells[col]);
if (data != null && data.isValid()) {
validCells.add(data..columnIndex = col);
}
}
// 1.
for (int col = 0; col < 4 && col < cells.length; col++) {
extendedCells.add(cells[col]);
}
final List<String> heRelations = []; //
final List<String> chongRelations = []; //
final List<String> xingRelations = []; //
// 2. Compare all valid cells pairwise
for (int i = 0; i < validCells.length; i++) {
for (int j = i + 1; j < validCells.length; j++) {
final cell1 = validCells[i];
final cell2 = validCells[j];
// Get column titles
final title1 = _columnTitles[cell1.columnIndex] ?? '';
final title2 = _columnTitles[cell2.columnIndex] ?? '';
// Check for harmony ()
if (_isHarmony(cell1, cell2)) {
heRelations.add('$title1$title2合');
}
// Check for conflict ()
if (_isConflict(cell1.zodiac, cell2.zodiac)) {
chongRelations.add('$title1$title2冲');
}
// Check for punishment ()
if (_isPunishment(cell1.zodiac, cell2.zodiac)) {
xingRelations.add('$title1$title2刑');
}
}
// 2. 5-18
for (int col = 4; col <= 17 && col < cells.length; col++) {
final title = _columnTitles[col] ?? '';
extendedCells.add('${title}');
extendedCells.add(cells[col]);
}
// 3. (19-21)
if (heRelations.isNotEmpty) {
sheet
.cell(CellIndex.indexByColumnRow(columnIndex: cells.length, rowIndex: rowIndex))
.value = TextCellValue(heRelations.join(' '));
}
if (chongRelations.isNotEmpty) {
sheet
.cell(CellIndex.indexByColumnRow(columnIndex: cells.length + 1, rowIndex: rowIndex))
.value = TextCellValue(chongRelations.join(' '));
}
if (xingRelations.isNotEmpty) {
sheet
.cell(CellIndex.indexByColumnRow(columnIndex: cells.length + 2, rowIndex: rowIndex))
.value = TextCellValue(xingRelations.join(' '));
}
}
CellData? _parseCellData(String cellValue) {
//
// 1. "12酉34"
// 2. '秒(如"10丑48'49""11°50'00")
final match = RegExp(
r"^(\d+)([^\d\s°\']+)(\d+)(?:[\'](\d{1,2}))?$",
).firstMatch(cellValue.trim());
if (match == null) return null;
final degreeStr = match.group(1);
final zodiac = match.group(2);
final minuteStr = match.group(3);
final secondStr = match.group(4); // null
if (degreeStr == null || zodiac == null || minuteStr == null) return null;
// double类型的度
final degree = double.tryParse(degreeStr);
final minute = double.tryParse(minuteStr);
final second = secondStr != null ? double.tryParse(secondStr) : 0.0;
if (degree == null || minute == null) return null;
// 60=1
final totalMinutes = minute + (second ?? 0) / 60;
return CellData(degree: degree, zodiac: zodiac, minute: totalMinutes);
}
bool _isHarmony(CellData cell1, CellData cell2) {
//
if (cell1.zodiac == cell2.zodiac) return false;
// 1. 1
final totalDiff = (cell1.totalMinutes - cell2.totalMinutes).abs();
final isDegreeMatch = totalDiff <= 60;
if (isDegreeMatch) {
// 1true
return true;
}
// 2. r
var isZodiacMatch = false;
for (final group in harmonyGroups) {
if (group.contains(cell1.zodiac) && group.contains(cell2.zodiac)) {
isZodiacMatch = true;
break;
}
}
//
return isDegreeMatch || isZodiacMatch;
}
bool _isConflict(String zodiac1, String zodiac2) {
//
if (zodiac1 == zodiac2) return false;
for (final group in conflictGroups) {
if (group.contains(zodiac1) && group.contains(zodiac2)) {
return true;
}
// 3.
extendedCells.addAll(['', '', '']);
//
for (int col = 0; col < extendedCells.length; col++) {
final cell = sheet.cell(CellIndex.indexByColumnRow(columnIndex: col, rowIndex: rowIndex));
cell.value = TextCellValue(extendedCells[col]);
cell.cellStyle = CellStyle(
backgroundColorHex: ExcelColor.lightGreen,
fontColorHex: ExcelColor.black,
bold: true,
);
}
return false;
}
bool _isPunishment(String zodiac1, String zodiac2) {
//
if (zodiac1 == zodiac2) return false;
for (final group in punishmentGroups) {
if (group.contains(zodiac1) && group.contains(zodiac2)) {
return true;
}
void _setColumnWidths(Sheet sheet) {
// + +
for (var i = 0; i < 4 + (_columnTitles.length * 2) + 3; i++) {
sheet.setColumnWidth(i, 8.0);
}
return false;
}
//
final List<bool> _columnRedStates = List.filled(14, false);
void _applyConditionalFormatting(Data cell, String value) {
if (value.contains('R')) {
_columnRedStates[cell.cellIndex.columnIndex] = true;
} else if (value.contains('D')) {
_columnRedStates[cell.cellIndex.columnIndex] = false;
}
Future<void> _saveExcelFile(Excel excel, String? fileName) async {
final bytes = excel.save()!;
final outputPath = await FilePicker.platform.saveFile(
dialogTitle: 'Save Excel File',
fileName: fileName ?? 'pdf_export_${DateTime.now().millisecondsSinceEpoch}.xlsx',
allowedExtensions: ['xlsx'],
type: FileType.custom,
);
if (_columnRedStates[cell.cellIndex.columnIndex]) {
cell.cellStyle = CellStyle(fontColorHex: ExcelColor.red);
if (outputPath != null) {
await File(outputPath).writeAsBytes(bytes);
Logger().info('文件已导出至: $outputPath');
} else {
Logger().info('User cancelled Excel export');
}
}
}
class CellData {
final double? degree;
final String zodiac;
final double? minute;
int columnIndex = -1;
//
static const validZodiacs = {'', '', '', '', '', '', '', '', '', '', '', ''};
CellData({this.degree, required this.zodiac, this.minute});
bool isValid() {
return degree != null && minute != null && validZodiacs.contains(zodiac);
}
double get totalMinutes => (degree ?? 0) * 60 + (minute ?? 0);
}

169
win_text_editor/lib/modules/pdf_parse/services/relationship_calculator_service.dart

@ -0,0 +1,169 @@ @@ -0,0 +1,169 @@
import 'package:excel/excel.dart';
class RelationshipCalculatorService {
final Map<String, bool> _columnRedStates = {};
final List<List<String>> _aspects = []; // Stores [conjunction, opposition, square] for each row
static const List<String> zodiacColumns = [
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
];
void processEntireSheet(Sheet sheet) {
_columnRedStates.clear();
_aspects.clear();
for (var rowIndex = 0; rowIndex < sheet.maxRows; rowIndex++) {
final row = sheet.rows[rowIndex];
if (row.isEmpty) continue;
// "Date"
if (row.first?.value?.toString().startsWith('Date') == true) {
continue;
}
_processZodiacColumns(rowIndex, row);
_calculateCoordinateRelationships(row);
}
// Add aspect markers to the last three rows
_addAspectMarkers(sheet);
}
void _processZodiacColumns(int rowIndex, List<Data?> row) {
for (int i = 0; i < zodiacColumns.length; i++) {
final columnTitle = zodiacColumns[i];
final dataColumnIndex = 4 + (i * 2) + 1;
if (dataColumnIndex >= row.length) continue;
final cell = row[dataColumnIndex];
if (cell == null) continue;
final cellValue = cell.value?.toString() ?? '';
if (cellValue.contains('R')) {
cell.cellStyle = CellStyle(fontColorHex: ExcelColor.red);
_columnRedStates[columnTitle] = true;
} else if (cellValue.contains('D')) {
cell.cellStyle = CellStyle(fontColorHex: ExcelColor.black);
_columnRedStates[columnTitle] = false;
} else if (_columnRedStates[columnTitle] == true) {
cell.cellStyle = CellStyle(fontColorHex: ExcelColor.red);
} else {
cell.cellStyle = CellStyle(fontColorHex: ExcelColor.black);
}
}
}
void _calculateCoordinateRelationships(List<Data?> row) {
List<int> positionsInMinutes = [];
List<String> planets = [];
// Extract positions for all planets in minutes
for (int i = 0; i < zodiacColumns.length; i++) {
final dataColumnIndex = 4 + (i * 2) + 1;
if (dataColumnIndex >= row.length) continue;
final cell = row[dataColumnIndex];
if (cell == null) continue;
final cellValue = cell.value?.toString() ?? '';
final cleanedValue = cellValue.replaceAll(RegExp(r'[RD]'), '');
if (cleanedValue.isEmpty) continue;
try {
final parts = cleanedValue.split('°');
if (parts.length != 2) continue;
final degrees = int.parse(parts[0]);
final minutesPart = parts[1].split('\'');
final minutes = minutesPart.isNotEmpty ? int.parse(minutesPart[0]) : 0;
positionsInMinutes.add(degrees * 60 + minutes);
planets.add(zodiacColumns[i]);
} catch (e) {
continue;
}
}
// Calculate aspects
List<String> conjunctions = [];
List<String> oppositions = [];
List<String> squares = [];
for (int i = 0; i < positionsInMinutes.length; i++) {
for (int j = i + 1; j < positionsInMinutes.length; j++) {
final diff = (positionsInMinutes[i] - positionsInMinutes[j]).abs();
final circularDiff = diff > 10800 ? 21600 - diff : diff; // 360° = 21600 minutes
// Check for conjunction (<60 minutes or 120°±30 minutes)
if (circularDiff < 60 || (circularDiff >= 7170 && circularDiff <= 7290)) {
// 119°30' to 120°29'
if (planets[i] == '' && planets[j] == '') {
//
} else {
conjunctions.add('${planets[i]}${planets[j]}');
}
}
// Check for square (90°±30 minutes)
else if (circularDiff >= 5370 && circularDiff <= 5490) {
// 89°30' to 90°29'
squares.add('${planets[i]}${planets[j]}');
}
// Check for opposition (180°±30 minutes)
else if (circularDiff >= 10770 && circularDiff <= 10890) {
// 179°30' to 180°29'
oppositions.add('${planets[i]}${planets[j]}');
}
}
}
_aspects.add([conjunctions.join(', '), oppositions.join(', '), squares.join(', ')]);
}
void _addAspectMarkers(Sheet sheet) {
if (_aspects.isEmpty) return;
//
final firstAspectCol = sheet.maxColumns - 3;
final secondAspectCol = sheet.maxColumns - 2;
final thirdAspectCol = sheet.maxColumns - 1;
for (int rowIndex = 0; rowIndex < sheet.maxRows; rowIndex++) {
final row = sheet.rows[rowIndex];
if (row.isEmpty || row.first?.value?.toString().startsWith('Date') == true) {
continue;
}
// aspect数据
int dataIndex = rowIndex - 1; //
if (dataIndex >= 0 && dataIndex < _aspects.length) {
final aspects = _aspects[dataIndex];
_setCellValue(sheet, firstAspectCol, rowIndex, aspects[0]);
_setCellValue(sheet, secondAspectCol, rowIndex, aspects[1]);
_setCellValue(sheet, thirdAspectCol, rowIndex, aspects[2]);
}
}
}
//
void _setCellValue(Sheet sheet, int col, int row, String value) {
final cell = sheet.cell(CellIndex.indexByColumnRow(columnIndex: col, rowIndex: row));
cell.value = TextCellValue(value); // 使TextCellValue包装字符串
}
}
Loading…
Cancel
Save