fix: banners entre sites
This commit is contained in:
parent
5db9199705
commit
3b0c93a35e
@ -17,9 +17,12 @@
|
||||
"WebFetch(domain:github.com)",
|
||||
"WebFetch(domain:gist.github.com)",
|
||||
"Bash(dotnet list:*)",
|
||||
"Bash(dotnet build:*)"
|
||||
"Bash(dotnet build:*)",
|
||||
"Bash(rm:*)",
|
||||
"Bash(dotnet add package:*)",
|
||||
"Bash(dir:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
319
Areas/DocumentConverters/Controllers/PdfBarcodeLineController.cs
Normal file
319
Areas/DocumentConverters/Controllers/PdfBarcodeLineController.cs
Normal file
@ -0,0 +1,319 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using iText.Kernel.Pdf;
|
||||
using iText.Kernel.Pdf.Canvas.Parser;
|
||||
using iText.Kernel.Pdf.Canvas.Parser.Listener;
|
||||
using iText.Kernel.Exceptions;
|
||||
|
||||
namespace Convert_It_Online.Areas.DocumentConverters.Controllers
|
||||
{
|
||||
[Area("DocumentConverters")]
|
||||
public class PdfBarcodeLineController : Controller
|
||||
{
|
||||
private readonly IStringLocalizer<SharedResource> _localizer;
|
||||
private readonly ILogger<PdfBarcodeLineController> _logger;
|
||||
private const long MaxPreviewSize = 5 * 1024 * 1024; // 5MB
|
||||
private static readonly Regex CandidatePattern = new(@"[0-9\s\.-]{44,70}", RegexOptions.Compiled);
|
||||
|
||||
public PdfBarcodeLineController(IStringLocalizer<SharedResource> localizer, ILogger<PdfBarcodeLineController> logger)
|
||||
{
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private void SetCommonViewBagProperties()
|
||||
{
|
||||
ViewBag.HomeLink = _localizer["HomeLink"];
|
||||
ViewBag.TextMenuTitle = _localizer["TextMenuTitle"];
|
||||
ViewBag.ImageMenuTitle = _localizer["ImageMenuTitle"];
|
||||
ViewBag.DocumentMenuTitle = _localizer["DocumentMenuTitle"];
|
||||
ViewBag.CaseConverterTitle = _localizer["CaseConverterTitle"];
|
||||
ViewBag.JpgToWebpTitle = _localizer["JpgToWebpTitle"];
|
||||
ViewBag.HeicToJpgTitle = _localizer["HeicToJpgTitle"];
|
||||
ViewBag.PdfToTextTitle = _localizer["PdfToTextTitle"];
|
||||
ViewBag.PdfBarcodeTitle = _localizer["PdfBarcodeTitle"];
|
||||
ViewBag.FooterText = _localizer["FooterText"];
|
||||
ViewBag.About = _localizer["About"];
|
||||
ViewBag.Contact = _localizer["Contact"];
|
||||
ViewBag.Terms = _localizer["Terms"];
|
||||
}
|
||||
|
||||
private void PrepareIndexView()
|
||||
{
|
||||
SetCommonViewBagProperties();
|
||||
ViewBag.PageTitle = _localizer["PdfBarcodeConverterPageTitle"];
|
||||
ViewBag.PageDescription = _localizer["PdfBarcodeConverterPageDescription"];
|
||||
ViewBag.PdfBarcodeDetectTabTitle = _localizer["PdfBarcodeDetectTabTitle"];
|
||||
ViewBag.PdfBarcodeDownloadTabTitle = _localizer["PdfBarcodeDownloadTabTitle"];
|
||||
ViewBag.PdfBarcodeFileInputLabel = _localizer["PdfBarcodeFileInputLabel"];
|
||||
ViewBag.PdfPasswordLabel = _localizer["PdfPasswordLabel"];
|
||||
ViewBag.PdfPasswordPlaceholder = _localizer["PdfPasswordPlaceholder"];
|
||||
ViewBag.PdfBarcodePasswordHint = _localizer["PdfBarcodePasswordHint"];
|
||||
ViewBag.DetectBarcodeButton = _localizer["DetectBarcodeButton"];
|
||||
ViewBag.DownloadBarcodeButton = _localizer["DownloadBarcodeButton"];
|
||||
ViewBag.PdfBarcodePreviewTitle = _localizer["PdfBarcodePreviewTitle"];
|
||||
ViewBag.SelectFileError = _localizer["SelectFileError"];
|
||||
ViewBag.CopyButtonLabel = _localizer["CopyButtonLabel"];
|
||||
ViewBag.CopyButtonSuccess = _localizer["CopyButtonSuccess"];
|
||||
|
||||
ViewBag.FaqWhatTitle = _localizer["PdfBarcodeFaqWhatTitle"];
|
||||
ViewBag.FaqWhatContent = _localizer["PdfBarcodeFaqWhatContent"];
|
||||
ViewBag.FaqHowTitle = _localizer["PdfBarcodeFaqHowTitle"];
|
||||
ViewBag.FaqHowContent = _localizer["PdfBarcodeFaqHowContent"];
|
||||
ViewBag.FaqWhyTitle = _localizer["PdfBarcodeFaqWhyTitle"];
|
||||
ViewBag.FaqWhyContent = _localizer["PdfBarcodeFaqWhyContent"];
|
||||
ViewBag.FaqSecurityTitle = _localizer["PdfBarcodeFaqSecurityTitle"];
|
||||
ViewBag.FaqSecurityContent = _localizer["PdfBarcodeFaqSecurityContent"];
|
||||
ViewBag.FaqLimitsTitle = _localizer["PdfBarcodeFaqLimitsTitle"];
|
||||
ViewBag.FaqLimitsContent = _localizer["PdfBarcodeFaqLimitsContent"];
|
||||
|
||||
ViewBag.MetaDescription = ViewBag.PageDescription;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
PrepareIndexView();
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> DetectLine(IFormFile pdfFile, string? password, bool preview = false)
|
||||
{
|
||||
return await HandleDetection(pdfFile, password, preview, forDownload: false);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> DownloadLine(IFormFile pdfFile, string? password, bool preview = false)
|
||||
{
|
||||
return await HandleDetection(pdfFile, password, preview, forDownload: true);
|
||||
}
|
||||
|
||||
private async Task<IActionResult> HandleDetection(IFormFile? pdfFile, string? password, bool preview, bool forDownload)
|
||||
{
|
||||
if (pdfFile == null || pdfFile.Length == 0)
|
||||
{
|
||||
_logger.LogWarning("[PDF-BARCODE] Attempt without file");
|
||||
if (preview)
|
||||
{
|
||||
return Json(new { success = false, message = _localizer["SelectFileError"].Value });
|
||||
}
|
||||
|
||||
ModelState.AddModelError("pdfFile", _localizer["SelectFileError"]);
|
||||
PrepareIndexView();
|
||||
return View("Index");
|
||||
}
|
||||
|
||||
if (!IsValidPdf(pdfFile))
|
||||
{
|
||||
_logger.LogWarning("[PDF-BARCODE] Invalid file type: {ContentType}", pdfFile.ContentType);
|
||||
if (preview)
|
||||
{
|
||||
return Json(new { success = false, message = _localizer["InvalidPdfFileError"].Value });
|
||||
}
|
||||
|
||||
ModelState.AddModelError("pdfFile", _localizer["InvalidPdfFileError"]);
|
||||
PrepareIndexView();
|
||||
return View("Index");
|
||||
}
|
||||
|
||||
if (preview && pdfFile.Length > MaxPreviewSize)
|
||||
{
|
||||
return Json(new { success = false, message = _localizer["PdfPreviewTooLarge"].Value });
|
||||
}
|
||||
|
||||
var extraction = await TryExtractTextAsync(pdfFile, password);
|
||||
if (!extraction.Success)
|
||||
{
|
||||
var message = _localizer[extraction.ErrorKey].Value;
|
||||
if (preview)
|
||||
{
|
||||
return Json(new { success = false, message });
|
||||
}
|
||||
|
||||
ModelState.AddModelError("pdfFile", _localizer[extraction.ErrorKey]);
|
||||
ViewBag.ConversionError = message;
|
||||
PrepareIndexView();
|
||||
return View("Index");
|
||||
}
|
||||
|
||||
var lineResult = FindBarcodeLine(extraction.Content ?? string.Empty);
|
||||
if (lineResult == null)
|
||||
{
|
||||
var message = _localizer["PdfNoBarcodeFound"].Value;
|
||||
if (preview)
|
||||
{
|
||||
return Json(new { success = false, message });
|
||||
}
|
||||
|
||||
ModelState.AddModelError("pdfFile", _localizer["PdfNoBarcodeFound"]);
|
||||
ViewBag.ConversionError = message;
|
||||
PrepareIndexView();
|
||||
return View("Index");
|
||||
}
|
||||
|
||||
if (preview)
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = true,
|
||||
content = lineResult,
|
||||
format = "digit-line"
|
||||
});
|
||||
}
|
||||
|
||||
var fileBaseName = Path.GetFileNameWithoutExtension(pdfFile.FileName);
|
||||
var downloadFileName = string.IsNullOrWhiteSpace(fileBaseName) ? "linha-digitavel.txt" : fileBaseName + "-linha-digitavel.txt";
|
||||
var payload = Encoding.UTF8.GetBytes(lineResult + Environment.NewLine);
|
||||
return File(payload, "text/plain", downloadFileName);
|
||||
}
|
||||
|
||||
private async Task<(bool Success, string? Content, string ErrorKey)> TryExtractTextAsync(IFormFile pdfFile, string? password)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var memoryStream = new MemoryStream();
|
||||
await pdfFile.CopyToAsync(memoryStream);
|
||||
var pdfBytes = memoryStream.ToArray();
|
||||
|
||||
// Try multiple password encodings
|
||||
var passwords = new List<byte[]?> { null }; // Start with no password
|
||||
|
||||
if (!string.IsNullOrEmpty(password))
|
||||
{
|
||||
passwords.Add(System.Text.Encoding.UTF8.GetBytes(password));
|
||||
passwords.Add(System.Text.Encoding.ASCII.GetBytes(password));
|
||||
passwords.Add(System.Text.Encoding.Latin1.GetBytes(password));
|
||||
}
|
||||
|
||||
foreach (var pwd in passwords)
|
||||
{
|
||||
try
|
||||
{
|
||||
ReaderProperties readerProperties = new ReaderProperties();
|
||||
if (pwd != null)
|
||||
{
|
||||
readerProperties.SetPassword(pwd);
|
||||
}
|
||||
|
||||
using var pdfReader = new PdfReader(new MemoryStream(pdfBytes), readerProperties);
|
||||
using var pdfDocument = new PdfDocument(pdfReader);
|
||||
|
||||
var builder = new StringBuilder();
|
||||
int numberOfPages = pdfDocument.GetNumberOfPages();
|
||||
|
||||
for (int i = 1; i <= numberOfPages; i++)
|
||||
{
|
||||
var page = pdfDocument.GetPage(i);
|
||||
|
||||
// Try multiple extraction strategies for barcode detection
|
||||
var strategies = new ITextExtractionStrategy[]
|
||||
{
|
||||
new LocationTextExtractionStrategy(),
|
||||
new SimpleTextExtractionStrategy()
|
||||
};
|
||||
|
||||
foreach (var strategy in strategies)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pageText = PdfTextExtractor.GetTextFromPage(page, strategy);
|
||||
if (!string.IsNullOrWhiteSpace(pageText))
|
||||
{
|
||||
builder.AppendLine(pageText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result = builder.ToString();
|
||||
if (!string.IsNullOrWhiteSpace(result))
|
||||
{
|
||||
return (true, result, string.Empty);
|
||||
}
|
||||
}
|
||||
catch (BadPasswordException) when (pwd != null)
|
||||
{
|
||||
continue; // Try next password encoding
|
||||
}
|
||||
catch (BadPasswordException) when (pwd == null && !string.IsNullOrEmpty(password))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(password))
|
||||
{
|
||||
return (false, null, "PdfInvalidPassword");
|
||||
}
|
||||
|
||||
return (false, null, "InvalidPdfFileError");
|
||||
}
|
||||
catch (BadPasswordException)
|
||||
{
|
||||
return (false, null, string.IsNullOrEmpty(password) ? "PdfPasswordRequired" : "PdfInvalidPassword");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "[PDF-BARCODE] Failed to extract text from {FileName}", pdfFile.FileName);
|
||||
return (false, null, "InvalidPdfFileError");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsValidPdf(IFormFile file)
|
||||
{
|
||||
if (file == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var contentType = file.ContentType?.ToLowerInvariant();
|
||||
if (contentType == "application/pdf" || contentType == "application/x-pdf")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Path.GetExtension(file.FileName).Equals(".pdf", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string? FindBarcodeLine(string rawText)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(rawText))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (Match match in CandidatePattern.Matches(rawText))
|
||||
{
|
||||
var digits = Regex.Replace(match.Value, "[^0-9]", string.Empty);
|
||||
if (digits.Length == 47 || digits.Length == 48 || digits.Length == 44)
|
||||
{
|
||||
return digits;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback: check full text digits
|
||||
var fullDigits = Regex.Replace(rawText, "[^0-9]", string.Empty);
|
||||
if (fullDigits.Length >= 44)
|
||||
{
|
||||
var candidates = new[] { 48, 47, 44 };
|
||||
foreach (var length in candidates)
|
||||
{
|
||||
if (fullDigits.Length >= length)
|
||||
{
|
||||
return fullDigits.Substring(0, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
318
Areas/DocumentConverters/Controllers/PdfToTextController.cs
Normal file
318
Areas/DocumentConverters/Controllers/PdfToTextController.cs
Normal file
@ -0,0 +1,318 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using iText.Kernel.Pdf;
|
||||
using iText.Kernel.Pdf.Canvas.Parser;
|
||||
using iText.Kernel.Pdf.Canvas.Parser.Listener;
|
||||
using iText.Kernel.Exceptions;
|
||||
|
||||
namespace Convert_It_Online.Areas.DocumentConverters.Controllers
|
||||
{
|
||||
[Area("DocumentConverters")]
|
||||
public class PdfToTextController : Controller
|
||||
{
|
||||
private readonly IStringLocalizer<SharedResource> _localizer;
|
||||
private readonly ILogger<PdfToTextController> _logger;
|
||||
private const long MaxPreviewSize = 10 * 1024 * 1024; // 10MB
|
||||
|
||||
public PdfToTextController(IStringLocalizer<SharedResource> localizer, ILogger<PdfToTextController> logger)
|
||||
{
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private void SetCommonViewBagProperties()
|
||||
{
|
||||
ViewBag.HomeLink = _localizer["HomeLink"];
|
||||
ViewBag.TextMenuTitle = _localizer["TextMenuTitle"];
|
||||
ViewBag.ImageMenuTitle = _localizer["ImageMenuTitle"];
|
||||
ViewBag.DocumentMenuTitle = _localizer["DocumentMenuTitle"];
|
||||
ViewBag.CaseConverterTitle = _localizer["CaseConverterTitle"];
|
||||
ViewBag.JpgToWebpTitle = _localizer["JpgToWebpTitle"];
|
||||
ViewBag.HeicToJpgTitle = _localizer["HeicToJpgTitle"];
|
||||
ViewBag.PdfToTextTitle = _localizer["PdfToTextTitle"];
|
||||
ViewBag.PdfBarcodeTitle = _localizer["PdfBarcodeTitle"];
|
||||
ViewBag.FooterText = _localizer["FooterText"];
|
||||
ViewBag.About = _localizer["About"];
|
||||
ViewBag.Contact = _localizer["Contact"];
|
||||
ViewBag.Terms = _localizer["Terms"];
|
||||
}
|
||||
|
||||
private void PrepareIndexView()
|
||||
{
|
||||
SetCommonViewBagProperties();
|
||||
ViewBag.PageTitle = _localizer["PdfTextConverterPageTitle"];
|
||||
ViewBag.PageDescription = _localizer["PdfTextConverterPageDescription"];
|
||||
ViewBag.PdfPlainTextTabTitle = _localizer["PdfPlainTextTabTitle"];
|
||||
ViewBag.PdfMarkdownTabTitle = _localizer["PdfMarkdownTabTitle"];
|
||||
ViewBag.PdfFileInputLabel = _localizer["PdfFileInputLabel"];
|
||||
ViewBag.PdfPasswordLabel = _localizer["PdfPasswordLabel"];
|
||||
ViewBag.PdfPasswordPlaceholder = _localizer["PdfPasswordPlaceholder"];
|
||||
ViewBag.PdfPasswordHint = _localizer["PdfPasswordHint"];
|
||||
ViewBag.ExtractPlainTextButton = _localizer["ExtractPlainTextButton"];
|
||||
ViewBag.ExtractMarkdownButton = _localizer["ExtractMarkdownButton"];
|
||||
ViewBag.DownloadPlainTextButton = _localizer["DownloadPlainTextButton"];
|
||||
ViewBag.DownloadMarkdownButton = _localizer["DownloadMarkdownButton"];
|
||||
ViewBag.PdfTextPreviewTitle = _localizer["PdfTextPreviewTitle"];
|
||||
ViewBag.SelectFileError = _localizer["SelectFileError"];
|
||||
|
||||
ViewBag.FaqWhatTitle = _localizer["PdfTextFaqWhatTitle"];
|
||||
ViewBag.FaqWhatContent = _localizer["PdfTextFaqWhatContent"];
|
||||
ViewBag.FaqHowTitle = _localizer["PdfTextFaqHowTitle"];
|
||||
ViewBag.FaqHowContent = _localizer["PdfTextFaqHowContent"];
|
||||
ViewBag.FaqWhyTitle = _localizer["PdfTextFaqWhyTitle"];
|
||||
ViewBag.FaqWhyContent = _localizer["PdfTextFaqWhyContent"];
|
||||
ViewBag.FaqSecurityTitle = _localizer["PdfTextFaqSecurityTitle"];
|
||||
ViewBag.FaqSecurityContent = _localizer["PdfTextFaqSecurityContent"];
|
||||
ViewBag.FaqLimitsTitle = _localizer["PdfTextFaqLimitsTitle"];
|
||||
ViewBag.FaqLimitsContent = _localizer["PdfTextFaqLimitsContent"];
|
||||
|
||||
ViewBag.MetaDescription = ViewBag.PageDescription;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
PrepareIndexView();
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Test()
|
||||
{
|
||||
return Json(new { success = true, message = "Roteamento funcionando!", timestamp = DateTime.Now });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> ExtractPlainText(IFormFile pdfFile, string? password, bool preview = false)
|
||||
{
|
||||
return await HandleExtraction(pdfFile, password, preview, toMarkdown: false);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> ExtractMarkdown(IFormFile pdfFile, string? password, bool preview = false)
|
||||
{
|
||||
return await HandleExtraction(pdfFile, password, preview, toMarkdown: true);
|
||||
}
|
||||
|
||||
private async Task<IActionResult> HandleExtraction(IFormFile? pdfFile, string? password, bool preview, bool toMarkdown)
|
||||
{
|
||||
if (pdfFile == null || pdfFile.Length == 0)
|
||||
{
|
||||
_logger.LogWarning("[PDF-TEXT] Attempt without file");
|
||||
if (preview)
|
||||
{
|
||||
return Json(new { success = false, message = _localizer["SelectFileError"].Value });
|
||||
}
|
||||
|
||||
ModelState.AddModelError("pdfFile", _localizer["SelectFileError"]);
|
||||
PrepareIndexView();
|
||||
return View("Index");
|
||||
}
|
||||
|
||||
if (!IsValidPdf(pdfFile))
|
||||
{
|
||||
_logger.LogWarning("[PDF-TEXT] Invalid file type: {ContentType}", pdfFile.ContentType);
|
||||
if (preview)
|
||||
{
|
||||
return Json(new { success = false, message = _localizer["InvalidPdfFileError"].Value });
|
||||
}
|
||||
|
||||
ModelState.AddModelError("pdfFile", _localizer["InvalidPdfFileError"]);
|
||||
PrepareIndexView();
|
||||
return View("Index");
|
||||
}
|
||||
|
||||
if (preview && pdfFile.Length > MaxPreviewSize)
|
||||
{
|
||||
if (preview)
|
||||
{
|
||||
return Json(new { success = false, message = _localizer["PdfPreviewTooLarge"].Value });
|
||||
}
|
||||
}
|
||||
|
||||
var extraction = await TryExtractTextAsync(pdfFile, password);
|
||||
if (!extraction.Success)
|
||||
{
|
||||
var message = _localizer[extraction.ErrorKey].Value;
|
||||
if (preview)
|
||||
{
|
||||
return Json(new { success = false, message });
|
||||
}
|
||||
|
||||
ModelState.AddModelError("pdfFile", _localizer[extraction.ErrorKey]);
|
||||
ViewBag.ConversionError = message;
|
||||
PrepareIndexView();
|
||||
return View("Index");
|
||||
}
|
||||
|
||||
var textContent = extraction.Content ?? string.Empty;
|
||||
if (toMarkdown)
|
||||
{
|
||||
textContent = ToMarkdown(textContent);
|
||||
}
|
||||
|
||||
var fileBaseName = Path.GetFileNameWithoutExtension(pdfFile.FileName);
|
||||
var extension = toMarkdown ? ".md" : ".txt";
|
||||
var downloadFileName = string.IsNullOrWhiteSpace(fileBaseName) ? (toMarkdown ? "resultado.md" : "resultado.txt") : fileBaseName + extension;
|
||||
var contentType = toMarkdown ? "text/markdown" : "text/plain";
|
||||
|
||||
if (preview)
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = true,
|
||||
content = textContent,
|
||||
filename = downloadFileName,
|
||||
format = toMarkdown ? "markdown" : "text"
|
||||
});
|
||||
}
|
||||
|
||||
var payload = Encoding.UTF8.GetBytes(textContent);
|
||||
return File(payload, contentType, downloadFileName);
|
||||
}
|
||||
|
||||
private async Task<(bool Success, string? Content, string ErrorKey)> TryExtractTextAsync(IFormFile pdfFile, string? password)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var memoryStream = new MemoryStream();
|
||||
await pdfFile.CopyToAsync(memoryStream);
|
||||
var pdfBytes = memoryStream.ToArray();
|
||||
|
||||
// Try multiple password encodings and extraction strategies
|
||||
var passwords = new List<byte[]?> { null }; // Start with no password
|
||||
|
||||
if (!string.IsNullOrEmpty(password))
|
||||
{
|
||||
passwords.Add(System.Text.Encoding.UTF8.GetBytes(password));
|
||||
passwords.Add(System.Text.Encoding.ASCII.GetBytes(password));
|
||||
passwords.Add(System.Text.Encoding.Latin1.GetBytes(password));
|
||||
}
|
||||
|
||||
foreach (var pwd in passwords)
|
||||
{
|
||||
try
|
||||
{
|
||||
ReaderProperties readerProperties = new ReaderProperties();
|
||||
if (pwd != null)
|
||||
{
|
||||
readerProperties.SetPassword(pwd);
|
||||
}
|
||||
|
||||
using var pdfReader = new PdfReader(new MemoryStream(pdfBytes), readerProperties);
|
||||
using var pdfDocument = new PdfDocument(pdfReader);
|
||||
|
||||
var builder = new StringBuilder();
|
||||
int numberOfPages = pdfDocument.GetNumberOfPages();
|
||||
|
||||
for (int i = 1; i <= numberOfPages; i++)
|
||||
{
|
||||
var page = pdfDocument.GetPage(i);
|
||||
|
||||
// Try multiple extraction strategies
|
||||
var strategies = new ITextExtractionStrategy[]
|
||||
{
|
||||
new LocationTextExtractionStrategy(),
|
||||
new SimpleTextExtractionStrategy()
|
||||
};
|
||||
|
||||
string pageText = "";
|
||||
foreach (var strategy in strategies)
|
||||
{
|
||||
try
|
||||
{
|
||||
pageText = PdfTextExtractor.GetTextFromPage(page, strategy);
|
||||
if (!string.IsNullOrWhiteSpace(pageText))
|
||||
break;
|
||||
}
|
||||
catch
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(pageText))
|
||||
{
|
||||
builder.AppendLine(pageText);
|
||||
builder.AppendLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"[Página {i} - texto não detectado ou pode conter apenas imagens]");
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
var result = builder.ToString().Trim();
|
||||
if (!string.IsNullOrWhiteSpace(result))
|
||||
{
|
||||
return (true, result, string.Empty);
|
||||
}
|
||||
}
|
||||
catch (BadPasswordException) when (pwd != null)
|
||||
{
|
||||
continue; // Try next password encoding
|
||||
}
|
||||
catch (BadPasswordException) when (pwd == null && !string.IsNullOrEmpty(password))
|
||||
{
|
||||
// Document requires password but none worked
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, either password was wrong or no text found
|
||||
if (!string.IsNullOrEmpty(password))
|
||||
{
|
||||
return (false, null, "PdfInvalidPassword");
|
||||
}
|
||||
|
||||
return (false, null, "InvalidPdfFileError");
|
||||
}
|
||||
catch (BadPasswordException)
|
||||
{
|
||||
return (false, null, string.IsNullOrEmpty(password) ? "PdfPasswordRequired" : "PdfInvalidPassword");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "[PDF-TEXT] Failed to extract text from {FileName}", pdfFile.FileName);
|
||||
return (false, null, "InvalidPdfFileError");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsValidPdf(IFormFile file)
|
||||
{
|
||||
if (file == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var contentType = file.ContentType?.ToLowerInvariant();
|
||||
if (contentType == "application/pdf" || contentType == "application/x-pdf")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Path.GetExtension(file.FileName).Equals(".pdf", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string ToMarkdown(string text)
|
||||
{
|
||||
var normalized = text.Replace("\r\n", "\n").Trim();
|
||||
if (string.IsNullOrWhiteSpace(normalized))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var builder = new StringBuilder();
|
||||
var lines = normalized.Split('\n');
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var trimmed = line.TrimEnd();
|
||||
builder.AppendLine(trimmed);
|
||||
}
|
||||
|
||||
return builder.ToString().Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
354
Areas/DocumentConverters/Views/PdfBarcodeLine/Index.cshtml
Normal file
354
Areas/DocumentConverters/Views/PdfBarcodeLine/Index.cshtml
Normal file
@ -0,0 +1,354 @@
|
||||
@{
|
||||
ViewData["Title"] = ViewBag.PageTitle;
|
||||
Layout = "_Layout";
|
||||
}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="text-center mb-4">
|
||||
<h1 class="display-5">@ViewBag.PageTitle</h1>
|
||||
<p class="lead text-muted">@ViewBag.PageDescription</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10 col-xl-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-upc-scan me-2"></i>@ViewBag.DocumentMenuTitle
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (ViewBag.ConversionError != null)
|
||||
{
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
@ViewBag.ConversionError
|
||||
</div>
|
||||
}
|
||||
|
||||
<ul class="nav nav-tabs mb-4" id="barcodeTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="detect-tab" data-bs-toggle="tab" data-bs-target="#detect" type="button" role="tab" aria-controls="detect" aria-selected="true">
|
||||
<i class="bi bi-eyeglasses me-2"></i>@ViewBag.PdfBarcodeDetectTabTitle
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="download-tab" data-bs-toggle="tab" data-bs-target="#download" type="button" role="tab" aria-controls="download" aria-selected="false">
|
||||
<i class="bi bi-download me-2"></i>@ViewBag.PdfBarcodeDownloadTabTitle
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="barcodeTabContent">
|
||||
<div class="tab-pane fade show active" id="detect" role="tabpanel" aria-labelledby="detect-tab">
|
||||
<form id="detectForm" action="/pt-BR/DocumentConverters/PdfBarcodeLine/DetectLine" method="post" enctype="multipart/form-data" class="needs-validation" novalidate>
|
||||
<div asp-validation-summary="ModelOnly" class="alert alert-danger" role="alert"></div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="detectPdfFile" class="form-label fw-semibold">@ViewBag.PdfBarcodeFileInputLabel</label>
|
||||
<input class="form-control" type="file" id="detectPdfFile" name="pdfFile" accept=".pdf" required>
|
||||
<div class="invalid-feedback">
|
||||
@ViewBag.SelectFileError
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="detectPassword" class="form-label fw-semibold">@ViewBag.PdfPasswordLabel</label>
|
||||
<input class="form-control" type="password" id="detectPassword" name="password" placeholder="@ViewBag.PdfPasswordPlaceholder">
|
||||
<div class="form-text">
|
||||
<i class="bi bi-shield-lock me-1"></i>@ViewBag.PdfBarcodePasswordHint
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button" id="previewDetectBtn" class="btn btn-primary">
|
||||
<i class="bi bi-upc-scan me-2"></i>@ViewBag.DetectBarcodeButton
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-download me-2"></i>@ViewBag.DownloadBarcodeButton
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="download" role="tabpanel" aria-labelledby="download-tab">
|
||||
<form id="downloadForm" action="/pt-BR/DocumentConverters/PdfBarcodeLine/DownloadLine" method="post" enctype="multipart/form-data" class="needs-validation" novalidate>
|
||||
<div asp-validation-summary="ModelOnly" class="alert alert-danger" role="alert"></div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="downloadPdfFile" class="form-label fw-semibold">@ViewBag.PdfBarcodeFileInputLabel</label>
|
||||
<input class="form-control" type="file" id="downloadPdfFile" name="pdfFile" accept=".pdf" required>
|
||||
<div class="invalid-feedback">
|
||||
@ViewBag.SelectFileError
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="downloadPassword" class="form-label fw-semibold">@ViewBag.PdfPasswordLabel</label>
|
||||
<input class="form-control" type="password" id="downloadPassword" name="password" placeholder="@ViewBag.PdfPasswordPlaceholder">
|
||||
<div class="form-text">
|
||||
<i class="bi bi-shield-lock me-1"></i>@ViewBag.PdfBarcodePasswordHint
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button" id="previewDownloadBtn" class="btn btn-primary">
|
||||
<i class="bi bi-eye me-2"></i>@ViewBag.DetectBarcodeButton
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-download me-2"></i>@ViewBag.DownloadBarcodeButton
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="barcodeLoading" class="text-center my-4" style="display: none;">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Carregando...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="barcodePreview" class="mt-4" style="display: none;">
|
||||
<h5 class="mb-3">@ViewBag.PdfBarcodePreviewTitle</h5>
|
||||
<div class="input-group">
|
||||
<input type="text" id="barcodeResult" class="form-control" readonly>
|
||||
<button id="copyBarcodeBtn" class="btn btn-outline-success" type="button">
|
||||
<i class="bi bi-clipboard-check me-1"></i>@ViewBag.CopyButtonLabel
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-end mt-3">
|
||||
<button id="downloadBarcodeBtn" class="btn btn-success btn-sm">
|
||||
<i class="bi bi-box-arrow-down me-2"></i>@ViewBag.DownloadBarcodeButton
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="barcodeError" class="mt-4" style="display: none;">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<i class="bi bi-exclamation-triangle me-2"></i>
|
||||
<span id="barcodeErrorMessage"></span>
|
||||
<button id="fallbackBarcodeBtn" class="btn btn-outline-primary btn-sm ms-2">
|
||||
@ViewBag.DownloadBarcodeButton
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-5">
|
||||
<div class="col-lg-10 mx-auto">
|
||||
<div class="converter-faq">
|
||||
<h3 class="h4 mb-3 text-center">Perguntas Frequentes</h3>
|
||||
<div class="accordion" id="barcodeFaqAccordion">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="barcodeFaqWhat">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#barcodeCollapseWhat" aria-expanded="false" aria-controls="barcodeCollapseWhat">
|
||||
<i class="bi bi-question-circle me-2"></i>@ViewBag.FaqWhatTitle
|
||||
</button>
|
||||
</h2>
|
||||
<div id="barcodeCollapseWhat" class="accordion-collapse collapse" aria-labelledby="barcodeFaqWhat" data-bs-parent="#barcodeFaqAccordion">
|
||||
<div class="accordion-body">
|
||||
@ViewBag.FaqWhatContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="barcodeFaqHow">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#barcodeCollapseHow" aria-expanded="false" aria-controls="barcodeCollapseHow">
|
||||
<i class="bi bi-gear me-2"></i>@ViewBag.FaqHowTitle
|
||||
</button>
|
||||
</h2>
|
||||
<div id="barcodeCollapseHow" class="accordion-collapse collapse" aria-labelledby="barcodeFaqHow" data-bs-parent="#barcodeFaqAccordion">
|
||||
<div class="accordion-body">
|
||||
@ViewBag.FaqHowContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="barcodeFaqWhy">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#barcodeCollapseWhy" aria-expanded="false" aria-controls="barcodeCollapseWhy">
|
||||
<i class="bi bi-lightbulb me-2"></i>@ViewBag.FaqWhyTitle
|
||||
</button>
|
||||
</h2>
|
||||
<div id="barcodeCollapseWhy" class="accordion-collapse collapse" aria-labelledby="barcodeFaqWhy" data-bs-parent="#barcodeFaqAccordion">
|
||||
<div class="accordion-body">
|
||||
@ViewBag.FaqWhyContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="barcodeFaqSecurity">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#barcodeCollapseSecurity" aria-expanded="false" aria-controls="barcodeCollapseSecurity">
|
||||
<i class="bi bi-shield-check me-2"></i>@ViewBag.FaqSecurityTitle
|
||||
</button>
|
||||
</h2>
|
||||
<div id="barcodeCollapseSecurity" class="accordion-collapse collapse" aria-labelledby="barcodeFaqSecurity" data-bs-parent="#barcodeFaqAccordion">
|
||||
<div class="accordion-body">
|
||||
@ViewBag.FaqSecurityContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="barcodeFaqLimits">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#barcodeCollapseLimits" aria-expanded="false" aria-controls="barcodeCollapseLimits">
|
||||
<i class="bi bi-exclamation-triangle me-2"></i>@ViewBag.FaqLimitsTitle
|
||||
</button>
|
||||
</h2>
|
||||
<div id="barcodeCollapseLimits" class="accordion-collapse collapse" aria-labelledby="barcodeFaqLimits" data-bs-parent="#barcodeFaqAccordion">
|
||||
<div class="accordion-body">
|
||||
@ViewBag.FaqLimitsContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#barcodeResult {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.spinner-border {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
(function () {
|
||||
const detectForm = document.getElementById('detectForm');
|
||||
const downloadForm = document.getElementById('downloadForm');
|
||||
const previewDetectBtn = document.getElementById('previewDetectBtn');
|
||||
const previewDownloadBtn = document.getElementById('previewDownloadBtn');
|
||||
const downloadBarcodeBtn = document.getElementById('downloadBarcodeBtn');
|
||||
const fallbackBarcodeBtn = document.getElementById('fallbackBarcodeBtn');
|
||||
const copyBarcodeBtn = document.getElementById('copyBarcodeBtn');
|
||||
const barcodeLoading = document.getElementById('barcodeLoading');
|
||||
const barcodePreview = document.getElementById('barcodePreview');
|
||||
const barcodeError = document.getElementById('barcodeError');
|
||||
const barcodeResult = document.getElementById('barcodeResult');
|
||||
const barcodeErrorMessage = document.getElementById('barcodeErrorMessage');
|
||||
|
||||
let currentLine = '';
|
||||
let currentFileName = 'linha-digitavel.txt';
|
||||
|
||||
const forms = document.querySelectorAll('.needs-validation');
|
||||
Array.from(forms).forEach(function (form) {
|
||||
form.addEventListener('submit', function (event) {
|
||||
if (!form.checkValidity()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
form.classList.add('was-validated');
|
||||
});
|
||||
});
|
||||
|
||||
previewDetectBtn.addEventListener('click', function () {
|
||||
handlePreview(detectForm);
|
||||
});
|
||||
|
||||
previewDownloadBtn.addEventListener('click', function () {
|
||||
handlePreview(downloadForm);
|
||||
});
|
||||
|
||||
downloadBarcodeBtn.addEventListener('click', function () {
|
||||
if (!currentLine) {
|
||||
return;
|
||||
}
|
||||
const blob = new Blob([currentLine + '\n'], { type: 'text/plain;charset=utf-8' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = url;
|
||||
anchor.download = currentFileName;
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
|
||||
fallbackBarcodeBtn.addEventListener('click', function () {
|
||||
const activeTab = document.querySelector('.nav-link.active');
|
||||
const currentForm = activeTab.id === 'detect-tab' ? detectForm : downloadForm;
|
||||
currentForm.submit();
|
||||
});
|
||||
|
||||
copyBarcodeBtn.addEventListener('click', function () {
|
||||
if (!currentLine) {
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(currentLine).then(function () {
|
||||
copyBarcodeBtn.innerHTML = '<i class="bi bi-clipboard-check-fill me-1"></i>@ViewBag.CopyButtonSuccess';
|
||||
setTimeout(function () {
|
||||
copyBarcodeBtn.innerHTML = '<i class="bi bi-clipboard-check me-1"></i>@ViewBag.CopyButtonLabel';
|
||||
}, 1500);
|
||||
});
|
||||
});
|
||||
|
||||
function handlePreview(form) {
|
||||
if (!form.checkValidity()) {
|
||||
form.classList.add('was-validated');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading();
|
||||
const formData = new FormData(form);
|
||||
|
||||
fetch(form.action + '?preview=true', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(async response => {
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
throw new Error(text || response.statusText);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || 'Erro na conversão');
|
||||
}
|
||||
|
||||
currentLine = result.content;
|
||||
currentFileName = (form === downloadForm ? 'linha-digitavel-download.txt' : 'linha-digitavel.txt');
|
||||
barcodeResult.value = currentLine;
|
||||
showPreview();
|
||||
}).catch(error => {
|
||||
showError(error.message || 'Erro inesperado.');
|
||||
});
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
barcodeLoading.style.display = 'block';
|
||||
barcodePreview.style.display = 'none';
|
||||
barcodeError.style.display = 'none';
|
||||
}
|
||||
|
||||
function showPreview() {
|
||||
barcodeLoading.style.display = 'none';
|
||||
barcodeError.style.display = 'none';
|
||||
barcodePreview.style.display = 'block';
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
barcodeLoading.style.display = 'none';
|
||||
barcodePreview.style.display = 'none';
|
||||
barcodeErrorMessage.textContent = message;
|
||||
barcodeError.style.display = 'block';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
}
|
||||
350
Areas/DocumentConverters/Views/PdfToText/Index.cshtml
Normal file
350
Areas/DocumentConverters/Views/PdfToText/Index.cshtml
Normal file
@ -0,0 +1,350 @@
|
||||
@{
|
||||
ViewData["Title"] = ViewBag.PageTitle;
|
||||
Layout = "_Layout";
|
||||
}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="text-center mb-4">
|
||||
<h1 class="display-5">@ViewBag.PageTitle</h1>
|
||||
<p class="lead text-muted">@ViewBag.PageDescription</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10 col-xl-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-file-earmark-text me-2"></i>@ViewBag.DocumentMenuTitle
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (ViewBag.ConversionError != null)
|
||||
{
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
@ViewBag.ConversionError
|
||||
</div>
|
||||
}
|
||||
|
||||
<ul class="nav nav-tabs mb-4" id="pdfTextTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="plain-text-tab" data-bs-toggle="tab" data-bs-target="#plain-text" type="button" role="tab" aria-controls="plain-text" aria-selected="true">
|
||||
<i class="bi bi-file-text me-2"></i>@ViewBag.PdfPlainTextTabTitle
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="markdown-tab" data-bs-toggle="tab" data-bs-target="#markdown" type="button" role="tab" aria-controls="markdown" aria-selected="false">
|
||||
<i class="bi bi-markdown me-2"></i>@ViewBag.PdfMarkdownTabTitle
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="pdfTextTabContent">
|
||||
<div class="tab-pane fade show active" id="plain-text" role="tabpanel" aria-labelledby="plain-text-tab">
|
||||
<form id="plainTextForm" action="/pt-BR/DocumentConverters/PdfToText/ExtractPlainText" method="post" enctype="multipart/form-data" class="needs-validation" novalidate>
|
||||
<div asp-validation-summary="ModelOnly" class="alert alert-danger" role="alert"></div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="plainTextPdfFile" class="form-label fw-semibold">@ViewBag.PdfFileInputLabel</label>
|
||||
<input class="form-control" type="file" id="plainTextPdfFile" name="pdfFile" accept=".pdf" required>
|
||||
<div class="invalid-feedback">
|
||||
@ViewBag.SelectFileError
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="plainTextPassword" class="form-label fw-semibold">@ViewBag.PdfPasswordLabel</label>
|
||||
<input class="form-control" type="password" id="plainTextPassword" name="password" placeholder="@ViewBag.PdfPasswordPlaceholder">
|
||||
<div class="form-text">
|
||||
<i class="bi bi-shield-lock me-1"></i>@ViewBag.PdfPasswordHint
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button" id="previewPlainTextBtn" class="btn btn-primary">
|
||||
<i class="bi bi-eye me-2"></i>@ViewBag.ExtractPlainTextButton
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-download me-2"></i>@ViewBag.DownloadPlainTextButton
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="markdown" role="tabpanel" aria-labelledby="markdown-tab">
|
||||
<form id="markdownForm" action="/pt-BR/DocumentConverters/PdfToText/ExtractMarkdown" method="post" enctype="multipart/form-data" class="needs-validation" novalidate>
|
||||
<div asp-validation-summary="ModelOnly" class="alert alert-danger" role="alert"></div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="markdownPdfFile" class="form-label fw-semibold">@ViewBag.PdfFileInputLabel</label>
|
||||
<input class="form-control" type="file" id="markdownPdfFile" name="pdfFile" accept=".pdf" required>
|
||||
<div class="invalid-feedback">
|
||||
@ViewBag.SelectFileError
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="markdownPassword" class="form-label fw-semibold">@ViewBag.PdfPasswordLabel</label>
|
||||
<input class="form-control" type="password" id="markdownPassword" name="password" placeholder="@ViewBag.PdfPasswordPlaceholder">
|
||||
<div class="form-text">
|
||||
<i class="bi bi-shield-lock me-1"></i>@ViewBag.PdfPasswordHint
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button" id="previewMarkdownBtn" class="btn btn-primary">
|
||||
<i class="bi bi-eye me-2"></i>@ViewBag.ExtractMarkdownButton
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-download me-2"></i>@ViewBag.DownloadMarkdownButton
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="loadingSpinner" class="text-center my-4" style="display: none;">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Carregando...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="previewArea" class="mt-4" style="display: none;">
|
||||
<h5 class="mb-3">@ViewBag.PdfTextPreviewTitle</h5>
|
||||
<textarea id="previewTextArea" class="form-control" rows="12" readonly></textarea>
|
||||
<div class="text-end mt-3">
|
||||
<button id="downloadPreviewBtn" class="btn btn-success btn-sm">
|
||||
<i class="bi bi-box-arrow-down me-2"></i>@ViewBag.DownloadPlainTextButton
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="errorArea" class="mt-4" style="display: none;">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<i class="bi bi-exclamation-triangle me-2"></i>
|
||||
<span id="errorMessage"></span>
|
||||
<button id="fallbackDownloadBtn" class="btn btn-outline-primary btn-sm ms-2">
|
||||
@ViewBag.DownloadPlainTextButton
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-5">
|
||||
<div class="col-lg-10 mx-auto">
|
||||
<div class="converter-faq">
|
||||
<h3 class="h4 mb-3 text-center">Perguntas Frequentes</h3>
|
||||
<div class="accordion" id="pdfTextFaqAccordion">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="faqWhat">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWhat" aria-expanded="false" aria-controls="collapseWhat">
|
||||
<i class="bi bi-question-circle me-2"></i>@ViewBag.FaqWhatTitle
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseWhat" class="accordion-collapse collapse" aria-labelledby="faqWhat" data-bs-parent="#pdfTextFaqAccordion">
|
||||
<div class="accordion-body">
|
||||
@ViewBag.FaqWhatContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="faqHow">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseHow" aria-expanded="false" aria-controls="collapseHow">
|
||||
<i class="bi bi-gear me-2"></i>@ViewBag.FaqHowTitle
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseHow" class="accordion-collapse collapse" aria-labelledby="faqHow" data-bs-parent="#pdfTextFaqAccordion">
|
||||
<div class="accordion-body">
|
||||
@ViewBag.FaqHowContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="faqWhy">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWhy" aria-expanded="false" aria-controls="collapseWhy">
|
||||
<i class="bi bi-lightbulb me-2"></i>@ViewBag.FaqWhyTitle
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseWhy" class="accordion-collapse collapse" aria-labelledby="faqWhy" data-bs-parent="#pdfTextFaqAccordion">
|
||||
<div class="accordion-body">
|
||||
@ViewBag.FaqWhyContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="faqSecurity">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSecurity" aria-expanded="false" aria-controls="collapseSecurity">
|
||||
<i class="bi bi-shield-check me-2"></i>@ViewBag.FaqSecurityTitle
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseSecurity" class="accordion-collapse collapse" aria-labelledby="faqSecurity" data-bs-parent="#pdfTextFaqAccordion">
|
||||
<div class="accordion-body">
|
||||
@ViewBag.FaqSecurityContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="faqLimits">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseLimits" aria-expanded="false" aria-controls="collapseLimits">
|
||||
<i class="bi bi-exclamation-triangle me-2"></i>@ViewBag.FaqLimitsTitle
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseLimits" class="accordion-collapse collapse" aria-labelledby="faqLimits" data-bs-parent="#pdfTextFaqAccordion">
|
||||
<div class="accordion-body">
|
||||
@ViewBag.FaqLimitsContent
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#previewTextArea {
|
||||
font-family: monospace;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.spinner-border {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
(function () {
|
||||
const plainForm = document.getElementById('plainTextForm');
|
||||
const markdownForm = document.getElementById('markdownForm');
|
||||
const previewPlainBtn = document.getElementById('previewPlainTextBtn');
|
||||
const previewMarkdownBtn = document.getElementById('previewMarkdownBtn');
|
||||
const downloadPreviewBtn = document.getElementById('downloadPreviewBtn');
|
||||
const fallbackDownloadBtn = document.getElementById('fallbackDownloadBtn');
|
||||
const loadingSpinner = document.getElementById('loadingSpinner');
|
||||
const previewArea = document.getElementById('previewArea');
|
||||
const errorArea = document.getElementById('errorArea');
|
||||
const previewTextArea = document.getElementById('previewTextArea');
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
const previewTitleButton = document.getElementById('downloadPreviewBtn');
|
||||
|
||||
let previewContent = '';
|
||||
let previewFileName = 'resultado.txt';
|
||||
let previewMime = 'text/plain';
|
||||
|
||||
const forms = document.querySelectorAll('.needs-validation');
|
||||
Array.from(forms).forEach(function (form) {
|
||||
form.addEventListener('submit', function (event) {
|
||||
if (!form.checkValidity()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
form.classList.add('was-validated');
|
||||
});
|
||||
});
|
||||
|
||||
previewPlainBtn.addEventListener('click', function () {
|
||||
handlePreview(plainForm, 'text/plain');
|
||||
});
|
||||
|
||||
previewMarkdownBtn.addEventListener('click', function () {
|
||||
handlePreview(markdownForm, 'text/markdown');
|
||||
});
|
||||
|
||||
downloadPreviewBtn.addEventListener('click', function () {
|
||||
if (!previewContent) {
|
||||
return;
|
||||
}
|
||||
const blob = new Blob([previewContent], { type: previewMime + ';charset=utf-8' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = url;
|
||||
anchor.download = previewFileName;
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
|
||||
fallbackDownloadBtn.addEventListener('click', function () {
|
||||
const activeTab = document.querySelector('.nav-link.active');
|
||||
const currentForm = activeTab.id === 'plain-text-tab' ? plainForm : markdownForm;
|
||||
currentForm.submit();
|
||||
});
|
||||
|
||||
function handlePreview(form, mimeType) {
|
||||
if (!form.checkValidity()) {
|
||||
form.classList.add('was-validated');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading();
|
||||
const formData = new FormData(form);
|
||||
const requestUrl = form.action + '?preview=true';
|
||||
|
||||
console.log('DEBUG: Requesting URL:', requestUrl);
|
||||
console.log('DEBUG: Form action:', form.action);
|
||||
|
||||
fetch(requestUrl, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(async response => {
|
||||
console.log('DEBUG: Response status:', response.status);
|
||||
console.log('DEBUG: Response headers:', response.headers.get('content-type'));
|
||||
|
||||
const responseText = await response.text();
|
||||
console.log('DEBUG: Response text (first 200 chars):', responseText.substring(0, 200));
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(responseText || response.statusText);
|
||||
}
|
||||
|
||||
const result = JSON.parse(responseText);
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || 'Erro na conversão');
|
||||
}
|
||||
|
||||
previewContent = result.content;
|
||||
previewFileName = result.filename;
|
||||
previewMime = mimeType;
|
||||
previewTextArea.value = result.content;
|
||||
previewTitleButton.innerHTML = `<i class="bi bi-box-arrow-down me-2"></i>${result.format === 'markdown' ? '@ViewBag.DownloadMarkdownButton' : '@ViewBag.DownloadPlainTextButton'}`;
|
||||
showPreview();
|
||||
}).catch(error => {
|
||||
showError(error.message || 'Erro inesperado.');
|
||||
});
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
loadingSpinner.style.display = 'block';
|
||||
previewArea.style.display = 'none';
|
||||
errorArea.style.display = 'none';
|
||||
}
|
||||
|
||||
function showPreview() {
|
||||
loadingSpinner.style.display = 'none';
|
||||
errorArea.style.display = 'none';
|
||||
previewArea.style.display = 'block';
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
loadingSpinner.style.display = 'none';
|
||||
previewArea.style.display = 'none';
|
||||
errorMessage.textContent = message;
|
||||
errorArea.style.display = 'block';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
}
|
||||
@ -27,6 +27,9 @@ namespace Convert_It_Online.Areas.ImageConverters.Controllers
|
||||
ViewBag.CaseConverterTitle = _localizer["CaseConverterTitle"];
|
||||
ViewBag.JpgToWebpTitle = _localizer["JpgToWebpTitle"];
|
||||
ViewBag.HeicToJpgTitle = _localizer["HeicToJpgTitle"];
|
||||
ViewBag.DocumentMenuTitle = _localizer["DocumentMenuTitle"];
|
||||
ViewBag.PdfToTextTitle = _localizer["PdfToTextTitle"];
|
||||
ViewBag.PdfBarcodeTitle = _localizer["PdfBarcodeTitle"];
|
||||
ViewBag.FooterText = _localizer["FooterText"];
|
||||
ViewBag.About = _localizer["About"];
|
||||
ViewBag.Contact = _localizer["Contact"];
|
||||
@ -56,6 +59,7 @@ namespace Convert_It_Online.Areas.ImageConverters.Controllers
|
||||
ViewBag.FaqSecurityContent = _localizer["HeicFaqSecurityContent"];
|
||||
ViewBag.FaqLimitsTitle = _localizer["HeicFaqLimitsTitle"];
|
||||
ViewBag.FaqLimitsContent = _localizer["HeicFaqLimitsContent"];
|
||||
ViewBag.MetaDescription = ViewBag.PageDescription;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
@ -270,4 +274,4 @@ namespace Convert_It_Online.Areas.ImageConverters.Controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,10 @@ namespace Convert_It_Online.Areas.ImageConverters.Controllers
|
||||
ViewBag.ImageMenuTitle = _localizer["ImageMenuTitle"];
|
||||
ViewBag.CaseConverterTitle = _localizer["CaseConverterTitle"];
|
||||
ViewBag.JpgToWebpTitle = _localizer["JpgToWebpTitle"];
|
||||
ViewBag.HeicToJpgTitle = _localizer["HeicToJpgTitle"];
|
||||
ViewBag.DocumentMenuTitle = _localizer["DocumentMenuTitle"];
|
||||
ViewBag.PdfToTextTitle = _localizer["PdfToTextTitle"];
|
||||
ViewBag.PdfBarcodeTitle = _localizer["PdfBarcodeTitle"];
|
||||
ViewBag.FooterText = _localizer["FooterText"];
|
||||
ViewBag.About = _localizer["About"];
|
||||
ViewBag.Contact = _localizer["Contact"];
|
||||
@ -54,6 +58,7 @@ namespace Convert_It_Online.Areas.ImageConverters.Controllers
|
||||
ViewBag.FaqSecurityContent = _localizer["JpgWebpFaqSecurityContent"];
|
||||
ViewBag.FaqLimitsTitle = _localizer["JpgWebpFaqLimitsTitle"];
|
||||
ViewBag.FaqLimitsContent = _localizer["JpgWebpFaqLimitsContent"];
|
||||
ViewBag.MetaDescription = ViewBag.PageDescription;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
@ -252,4 +257,4 @@ namespace Convert_It_Online.Areas.ImageConverters.Controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +37,10 @@ namespace Convert_It_Online.Areas.TextTools.Controllers
|
||||
ViewBag.ImageMenuTitle = _localizer["ImageMenuTitle"];
|
||||
ViewBag.CaseConverterTitle = _localizer["CaseConverterTitle"];
|
||||
ViewBag.JpgToWebpTitle = _localizer["JpgToWebpTitle"];
|
||||
ViewBag.HeicToJpgTitle = _localizer["HeicToJpgTitle"];
|
||||
ViewBag.DocumentMenuTitle = _localizer["DocumentMenuTitle"];
|
||||
ViewBag.PdfToTextTitle = _localizer["PdfToTextTitle"];
|
||||
ViewBag.PdfBarcodeTitle = _localizer["PdfBarcodeTitle"];
|
||||
ViewBag.FooterText = _localizer["FooterText"];
|
||||
ViewBag.About = _localizer["About"];
|
||||
ViewBag.Contact = _localizer["Contact"];
|
||||
@ -66,6 +70,7 @@ namespace Convert_It_Online.Areas.TextTools.Controllers
|
||||
ViewBag.FaqSecurityContent = _localizer["CaseFaqSecurityContent"];
|
||||
ViewBag.FaqLimitsTitle = _localizer["CaseFaqLimitsTitle"];
|
||||
ViewBag.FaqLimitsContent = _localizer["CaseFaqLimitsContent"];
|
||||
ViewBag.MetaDescription = ViewBag.PageTitle;
|
||||
|
||||
var model = new CaseConverterViewModel();
|
||||
return View(model);
|
||||
@ -83,6 +88,7 @@ namespace Convert_It_Online.Areas.TextTools.Controllers
|
||||
ViewBag.ToLowerButton = _localizer["ToLowerButton"];
|
||||
ViewBag.ToSentenceCaseButton = _localizer["ToSentenceCaseButton"];
|
||||
ViewBag.ResultTitle = _localizer["ResultTitle"];
|
||||
ViewBag.MetaDescription = ViewBag.PageTitle;
|
||||
|
||||
if (!ModelState.IsValid || string.IsNullOrEmpty(model.InputText))
|
||||
{
|
||||
|
||||
10
CLAUDE.md
10
CLAUDE.md
@ -13,10 +13,12 @@ Convert-It Online é uma aplicação web ASP.NET Core que oferece ferramentas gr
|
||||
**Português (pt-BR):**
|
||||
- Ferramentas de Texto: `/pt-BR/ferramentas-de-texto/conversor-de-maiusculas-minusculas`
|
||||
- Conversores de Imagem: `/pt-BR/conversores-de-imagem/jpg-para-webp`
|
||||
- Conversores de Documento: `/pt-BR/conversores-de-documento/pdf-para-texto`
|
||||
|
||||
**Espanhol (es-MX, es-CL, es-PY):**
|
||||
- Herramientas de Texto: `/es-MX/herramientas-de-texto/conversor-de-mayusculas-minusculas`
|
||||
- Convertidores de Imagen: `/es-MX/convertidores-de-imagen/jpg-a-webp`
|
||||
- Convertidores de Documento: `/es-MX/convertidores-de-documento/pdf-a-texto`
|
||||
|
||||
### Arquitetura do Sistema de URLs
|
||||
|
||||
@ -53,6 +55,12 @@ Convert-It Online é uma aplicação web ASP.NET Core que oferece ferramentas gr
|
||||
- **URL PT**: `/pt-BR/conversores-de-imagem/jpg-para-webp`
|
||||
- **URL ES**: `/es-MX/convertidores-de-imagen/jpg-a-webp`
|
||||
|
||||
### DocumentConverters
|
||||
- **Controllers**: `PdfToTextController`, `PdfBarcodeLineController`
|
||||
- **Funcionalidades**: Extrair texto/Markdown de PDFs e detectar linha digitável de boletos
|
||||
- **URL PT**: `/pt-BR/conversores-de-documento/pdf-para-texto` e `/pt-BR/conversores-de-documento/linha-digitavel-do-pdf`
|
||||
- **URL ES**: `/es-MX/convertidores-de-documento/pdf-a-texto` e `/es-MX/convertidores-de-documento/linea-digitada-desde-pdf`
|
||||
|
||||
## Desenvolvimento Guidelines
|
||||
|
||||
### Adicionando Novos Conversores
|
||||
@ -86,4 +94,4 @@ Convert-It Online é uma aplicação web ASP.NET Core que oferece ferramentas gr
|
||||
🔄 Aguardando testes finais
|
||||
|
||||
---
|
||||
*Mantenha este arquivo atualizado quando adicionar novos conversores ou idiomas.*
|
||||
*Mantenha este arquivo atualizado quando adicionar novos conversores ou idiomas.*
|
||||
|
||||
@ -32,6 +32,9 @@ namespace Convert_It_Online.Controllers
|
||||
ViewBag.CaseConverterTitle = _localizer["CaseConverterTitle"];
|
||||
ViewBag.JpgToWebpTitle = _localizer["JpgToWebpTitle"];
|
||||
ViewBag.HeicToJpgTitle = _localizer["HeicToJpgTitle"];
|
||||
ViewBag.DocumentMenuTitle = _localizer["DocumentMenuTitle"];
|
||||
ViewBag.PdfToTextTitle = _localizer["PdfToTextTitle"];
|
||||
ViewBag.PdfBarcodeTitle = _localizer["PdfBarcodeTitle"];
|
||||
ViewBag.FooterText = _localizer["FooterText"];
|
||||
ViewBag.About = _localizer["About"];
|
||||
ViewBag.Contact = _localizer["Contact"];
|
||||
@ -51,6 +54,7 @@ namespace Convert_It_Online.Controllers
|
||||
ViewBag.SecurityContent = _localizer["SecurityContent"];
|
||||
ViewBag.AllConvertersTitle = _localizer["AllConvertersTitle"];
|
||||
ViewBag.UseConverterButton = _localizer["UseConverterButton"];
|
||||
ViewBag.MetaDescription = ViewBag.Subtitle;
|
||||
|
||||
var individualConverters = new List<ToolViewModel>
|
||||
{
|
||||
@ -77,6 +81,22 @@ namespace Convert_It_Online.Controllers
|
||||
Area = "ImageConverters",
|
||||
Controller = "HeicToJpg",
|
||||
IconClass = "bi-phone"
|
||||
},
|
||||
new ToolViewModel
|
||||
{
|
||||
Title = _localizer["PdfToTextIndividualTitle"],
|
||||
Description = _localizer["PdfToTextIndividualDescription"],
|
||||
Area = "DocumentConverters",
|
||||
Controller = "PdfToText",
|
||||
IconClass = "bi-file-text"
|
||||
},
|
||||
new ToolViewModel
|
||||
{
|
||||
Title = _localizer["PdfBarcodeIndividualTitle"],
|
||||
Description = _localizer["PdfBarcodeIndividualDescription"],
|
||||
Area = "DocumentConverters",
|
||||
Controller = "PdfBarcodeLine",
|
||||
IconClass = "bi-upc-scan"
|
||||
}
|
||||
};
|
||||
|
||||
@ -97,6 +117,7 @@ namespace Convert_It_Online.Controllers
|
||||
ViewBag.Feature3 = _localizer["Feature3"];
|
||||
ViewBag.Feature4 = _localizer["Feature4"];
|
||||
ViewBag.StartConverting = _localizer["StartConverting"];
|
||||
ViewBag.MetaDescription = ViewBag.WhatIsContent;
|
||||
return View();
|
||||
}
|
||||
|
||||
@ -109,6 +130,7 @@ namespace Convert_It_Online.Controllers
|
||||
ViewBag.SupportTitle = _localizer["SupportTitle"];
|
||||
ViewBag.SupportTime = _localizer["SupportTime"];
|
||||
ViewBag.FaqTitle = _localizer["FaqTitle"];
|
||||
ViewBag.MetaDescription = ViewBag.ContactIntro;
|
||||
return View();
|
||||
}
|
||||
|
||||
@ -129,7 +151,8 @@ namespace Convert_It_Online.Controllers
|
||||
ViewBag.ChangesContent = _localizer["ChangesContent"];
|
||||
ViewBag.LastUpdated = _localizer["LastUpdated"];
|
||||
ViewBag.BackToHome = _localizer["BackToHome"];
|
||||
ViewBag.MetaDescription = ViewBag.TermsIntro;
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
@ -11,6 +11,7 @@
|
||||
<PackageReference Include="HeyRed.ImageSharp.Heif" Version="2.1.3" />
|
||||
<PackageReference Include="LibHeif.Native.win-x64" Version="1.15.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageReference Include="itext7" Version="8.0.5" />
|
||||
<PackageReference Include="Serilog" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||
@ -24,4 +25,4 @@
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@ -34,17 +34,25 @@ namespace Convert_It_Online.Middleware
|
||||
var translatedArea = match.Groups[2].Value;
|
||||
var translatedController = match.Groups[3].Value;
|
||||
var action = match.Groups[4].Success ? match.Groups[4].Value : "Index";
|
||||
|
||||
|
||||
// Skip if this is already an internal area/controller name (starts with capital letter)
|
||||
if (char.IsUpper(translatedArea[0]) || char.IsUpper(translatedController[0]))
|
||||
{
|
||||
_logger.LogInformation($"Skipping internal path: {path}");
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Matched: culture={cultureName}, area={translatedArea}, controller={translatedController}, action={action}");
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var culture = new CultureInfo(cultureName);
|
||||
var originalArea = _urlTranslationService.GetOriginalArea(translatedArea, culture);
|
||||
var originalController = _urlTranslationService.GetOriginalController(translatedController, culture);
|
||||
|
||||
|
||||
_logger.LogInformation($"Translations: originalArea={originalArea}, originalController={originalController}");
|
||||
|
||||
|
||||
if (originalArea != null && originalController != null)
|
||||
{
|
||||
// Rewrite the path to use original controller names
|
||||
@ -53,7 +61,7 @@ namespace Convert_It_Online.Middleware
|
||||
{
|
||||
newPath += $"/{action}";
|
||||
}
|
||||
|
||||
|
||||
_logger.LogInformation($"Rewriting path from {path} to {newPath}");
|
||||
context.Request.Path = newPath;
|
||||
}
|
||||
|
||||
@ -16,22 +16,26 @@ namespace Convert_It_Online.Services
|
||||
["pt-BR"] = new Dictionary<string, string>
|
||||
{
|
||||
["TextTools"] = "ferramentas-de-texto",
|
||||
["ImageConverters"] = "conversores-de-imagem"
|
||||
["ImageConverters"] = "conversores-de-imagem",
|
||||
["DocumentConverters"] = "conversores-de-documento"
|
||||
},
|
||||
["es-MX"] = new Dictionary<string, string>
|
||||
{
|
||||
["TextTools"] = "herramientas-de-texto",
|
||||
["ImageConverters"] = "convertidores-de-imagen"
|
||||
["ImageConverters"] = "convertidores-de-imagen",
|
||||
["DocumentConverters"] = "convertidores-de-documento"
|
||||
},
|
||||
["es-CL"] = new Dictionary<string, string>
|
||||
{
|
||||
["TextTools"] = "herramientas-de-texto",
|
||||
["ImageConverters"] = "convertidores-de-imagen"
|
||||
["ImageConverters"] = "convertidores-de-imagen",
|
||||
["DocumentConverters"] = "convertidores-de-documento"
|
||||
},
|
||||
["es-PY"] = new Dictionary<string, string>
|
||||
{
|
||||
["TextTools"] = "herramientas-de-texto",
|
||||
["ImageConverters"] = "convertidores-de-imagen"
|
||||
["ImageConverters"] = "convertidores-de-imagen",
|
||||
["DocumentConverters"] = "convertidores-de-documento"
|
||||
}
|
||||
};
|
||||
|
||||
@ -41,25 +45,33 @@ namespace Convert_It_Online.Services
|
||||
{
|
||||
["CaseConverter"] = "conversor-de-maiusculas-minusculas",
|
||||
["JpgToWebp"] = "jpg-para-webp",
|
||||
["HeicToJpg"] = "heic-para-jpg"
|
||||
["HeicToJpg"] = "heic-para-jpg",
|
||||
["PdfToText"] = "pdf-para-texto",
|
||||
["PdfBarcodeLine"] = "linha-digitavel-do-pdf"
|
||||
},
|
||||
["es-MX"] = new Dictionary<string, string>
|
||||
{
|
||||
["CaseConverter"] = "conversor-de-mayusculas-minusculas",
|
||||
["JpgToWebp"] = "jpg-a-webp",
|
||||
["HeicToJpg"] = "heic-a-jpg"
|
||||
["HeicToJpg"] = "heic-a-jpg",
|
||||
["PdfToText"] = "pdf-a-texto",
|
||||
["PdfBarcodeLine"] = "linea-digitada-desde-pdf"
|
||||
},
|
||||
["es-CL"] = new Dictionary<string, string>
|
||||
{
|
||||
["CaseConverter"] = "conversor-de-mayusculas-minusculas",
|
||||
["JpgToWebp"] = "jpg-a-webp",
|
||||
["HeicToJpg"] = "heic-a-jpg"
|
||||
["HeicToJpg"] = "heic-a-jpg",
|
||||
["PdfToText"] = "pdf-a-texto",
|
||||
["PdfBarcodeLine"] = "linea-digitada-desde-pdf"
|
||||
},
|
||||
["es-PY"] = new Dictionary<string, string>
|
||||
{
|
||||
["CaseConverter"] = "conversor-de-mayusculas-minusculas",
|
||||
["JpgToWebp"] = "jpg-a-webp",
|
||||
["HeicToJpg"] = "heic-a-jpg"
|
||||
["HeicToJpg"] = "heic-a-jpg",
|
||||
["PdfToText"] = "pdf-a-texto",
|
||||
["PdfBarcodeLine"] = "linea-digitada-desde-pdf"
|
||||
}
|
||||
};
|
||||
|
||||
@ -145,4 +157,4 @@ namespace Convert_It_Online.Services
|
||||
return $"/{culture.Name}/{translatedArea}/{translatedController}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,12 +51,21 @@
|
||||
<data name="ImageMenuTitle" xml:space="preserve">
|
||||
<value>Imagen</value>
|
||||
</data>
|
||||
<data name="DocumentMenuTitle" xml:space="preserve">
|
||||
<value>Documentos</value>
|
||||
</data>
|
||||
<data name="CaseConverterTitle" xml:space="preserve">
|
||||
<value>Convertidor de Mayúsculas/Minúsculas</value>
|
||||
</data>
|
||||
<data name="JpgToWebpTitle" xml:space="preserve">
|
||||
<value>JPG a WebP</value>
|
||||
</data>
|
||||
<data name="PdfToTextTitle" xml:space="preserve">
|
||||
<value>PDF a Texto/Markdown</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeTitle" xml:space="preserve">
|
||||
<value>Detectar línea digitada</value>
|
||||
</data>
|
||||
|
||||
<!-- Footer -->
|
||||
<data name="FooterText" xml:space="preserve">
|
||||
@ -113,12 +122,24 @@
|
||||
<data name="UrlImageConverters" xml:space="preserve">
|
||||
<value>convertidores-de-imagen</value>
|
||||
</data>
|
||||
<data name="UrlDocumentConverters" xml:space="preserve">
|
||||
<value>convertidores-de-documento</value>
|
||||
</data>
|
||||
<data name="UrlCaseConverter" xml:space="preserve">
|
||||
<value>conversor-de-mayusculas-minusculas</value>
|
||||
</data>
|
||||
<data name="UrlJpgToWebp" xml:space="preserve">
|
||||
<value>jpg-a-webp</value>
|
||||
</data>
|
||||
<data name="UrlHeicToJpg" xml:space="preserve">
|
||||
<value>heic-a-jpg</value>
|
||||
</data>
|
||||
<data name="UrlPdfToText" xml:space="preserve">
|
||||
<value>pdf-a-texto</value>
|
||||
</data>
|
||||
<data name="UrlPdfBarcode" xml:space="preserve">
|
||||
<value>linea-digitada-desde-pdf</value>
|
||||
</data>
|
||||
|
||||
<!-- New individual converter resources - Mexican Spanish -->
|
||||
<data name="AllConvertersTitle" xml:space="preserve">
|
||||
@ -142,6 +163,18 @@
|
||||
<data name="HeicToJpgIndividualDescription" xml:space="preserve">
|
||||
<value>Convierte fotos HEIC del iPhone al formato JPG universalmente compatible.</value>
|
||||
</data>
|
||||
<data name="PdfToTextIndividualTitle" xml:space="preserve">
|
||||
<value>Extraer texto de PDF</value>
|
||||
</data>
|
||||
<data name="PdfToTextIndividualDescription" xml:space="preserve">
|
||||
<value>Transforma tus PDFs en texto simple o Markdown, incluso cuando requieren contraseña.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeIndividualTitle" xml:space="preserve">
|
||||
<value>Detectar línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeIndividualDescription" xml:space="preserve">
|
||||
<value>Obtén la línea digitada de boletos directamente desde el PDF, con soporte para archivos protegidos.</value>
|
||||
</data>
|
||||
|
||||
<!-- HEIC converter menu item -->
|
||||
<data name="HeicToJpgTitle" xml:space="preserve">
|
||||
@ -204,16 +237,167 @@
|
||||
<value>No hay límites en la cantidad de conversiones. Para garantizar un buen rendimiento, recomendamos archivos de hasta 10MB. El procesamiento es más rápido en computadoras modernas.</value>
|
||||
</data>
|
||||
|
||||
<!-- PDF to Text Converter Resources -->
|
||||
<data name="PdfTextConverterPageTitle" xml:space="preserve">
|
||||
<value>Extraer texto de PDF</value>
|
||||
</data>
|
||||
<data name="PdfTextConverterPageDescription" xml:space="preserve">
|
||||
<value>Convierte tus PDFs en texto simple o Markdown con soporte para archivos protegidos con contraseña.</value>
|
||||
</data>
|
||||
<data name="PdfPlainTextTabTitle" xml:space="preserve">
|
||||
<value>PDF → Texto simple</value>
|
||||
</data>
|
||||
<data name="PdfMarkdownTabTitle" xml:space="preserve">
|
||||
<value>PDF → Markdown</value>
|
||||
</data>
|
||||
<data name="PdfFileInputLabel" xml:space="preserve">
|
||||
<value>Seleccionar archivo PDF</value>
|
||||
</data>
|
||||
<data name="PdfPasswordLabel" xml:space="preserve">
|
||||
<value>Contraseña del PDF</value>
|
||||
</data>
|
||||
<data name="PdfPasswordPlaceholder" xml:space="preserve">
|
||||
<value>Ingresa la contraseña si el PDF está protegido</value>
|
||||
</data>
|
||||
<data name="PdfPasswordHint" xml:space="preserve">
|
||||
<value>Si el PDF requiere contraseña de apertura, escríbela aquí. De lo contrario, deja el campo vacío.</value>
|
||||
</data>
|
||||
<data name="ExtractPlainTextButton" xml:space="preserve">
|
||||
<value>Extraer y visualizar</value>
|
||||
</data>
|
||||
<data name="ExtractMarkdownButton" xml:space="preserve">
|
||||
<value>Extraer en Markdown</value>
|
||||
</data>
|
||||
<data name="DownloadPlainTextButton" xml:space="preserve">
|
||||
<value>Descargar texto</value>
|
||||
</data>
|
||||
<data name="DownloadMarkdownButton" xml:space="preserve">
|
||||
<value>Descargar Markdown</value>
|
||||
</data>
|
||||
<data name="PdfTextPreviewTitle" xml:space="preserve">
|
||||
<value>Vista previa del contenido</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhatTitle" xml:space="preserve">
|
||||
<value>¿Qué hace este conversor?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhatContent" xml:space="preserve">
|
||||
<value>Extrae el contenido textual del PDF y lo entrega como texto simple o Markdown, preservando cortes y secciones siempre que sea posible.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqHowTitle" xml:space="preserve">
|
||||
<value>¿Cómo usar el conversor?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqHowContent" xml:space="preserve">
|
||||
<value>Selecciona el archivo PDF, ingresa la contraseña si es necesario y elige la pestaña deseada. Visualiza el resultado en línea o descarga el archivo.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhyTitle" xml:space="preserve">
|
||||
<value>¿Por qué convertir PDF a texto o Markdown?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhyContent" xml:space="preserve">
|
||||
<value>El texto simple permite ediciones rápidas, mientras que Markdown facilita reutilizar el contenido en blogs o documentación.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqSecurityTitle" xml:space="preserve">
|
||||
<value>¿Es seguro subir mis PDFs?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqSecurityContent" xml:space="preserve">
|
||||
<value>Sí. Procesamos los archivos siguiendo estrictas políticas de privacidad y los eliminamos tras la conversión.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqLimitsTitle" xml:space="preserve">
|
||||
<value>¿Existen límites de tamaño?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqLimitsContent" xml:space="preserve">
|
||||
<value>Para la vista previa recomendamos PDFs de hasta 10MB. Archivos mayores pueden descargarse directamente en texto o Markdown.</value>
|
||||
</data>
|
||||
|
||||
<!-- PDF Barcode Converter Resources -->
|
||||
<data name="PdfBarcodeConverterPageTitle" xml:space="preserve">
|
||||
<value>Extraer línea digitada de PDF</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeConverterPageDescription" xml:space="preserve">
|
||||
<value>Detecta la línea digitada de boletos o facturas directamente desde el PDF, incluso si está protegido.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeDetectTabTitle" xml:space="preserve">
|
||||
<value>PDF → Ver línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeDownloadTabTitle" xml:space="preserve">
|
||||
<value>PDF → Descargar línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFileInputLabel" xml:space="preserve">
|
||||
<value>Seleccionar PDF del boleto</value>
|
||||
</data>
|
||||
<data name="PdfBarcodePasswordHint" xml:space="preserve">
|
||||
<value>Si el PDF está protegido con contraseña, ingrésala para permitir la lectura.</value>
|
||||
</data>
|
||||
<data name="DetectBarcodeButton" xml:space="preserve">
|
||||
<value>Detectar línea digitada</value>
|
||||
</data>
|
||||
<data name="DownloadBarcodeButton" xml:space="preserve">
|
||||
<value>Descargar línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodePreviewTitle" xml:space="preserve">
|
||||
<value>Línea digitada detectada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhatTitle" xml:space="preserve">
|
||||
<value>¿Qué es la línea digitada?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhatContent" xml:space="preserve">
|
||||
<value>Es la secuencia numérica del código de barras del boleto, utilizada para realizar pagos en bancos y aplicaciones.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqHowTitle" xml:space="preserve">
|
||||
<value>¿Cómo funciona el conversor?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqHowContent" xml:space="preserve">
|
||||
<value>Extrae el texto del PDF, identifica patrones de boletos (47 o 48 dígitos) y muestra la línea lista para copiar o descargar.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhyTitle" xml:space="preserve">
|
||||
<value>¿Cuándo usar este recurso?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhyContent" xml:space="preserve">
|
||||
<value>Ideal para boletos sin código de barras legible o cuando necesitas copiar la línea digitada para pagar en línea.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqSecurityTitle" xml:space="preserve">
|
||||
<value>¿Mis datos están seguros?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqSecurityContent" xml:space="preserve">
|
||||
<value>Sí. El PDF se procesa solo para extraer la línea digitada y se descarta inmediatamente después.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqLimitsTitle" xml:space="preserve">
|
||||
<value>¿Existen limitaciones?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqLimitsContent" xml:space="preserve">
|
||||
<value>Para mejores resultados utiliza PDFs con texto seleccionable. Boletos en formato de imagen pueden requerir una solución OCR externa.</value>
|
||||
</data>
|
||||
|
||||
<!-- Error Messages -->
|
||||
<data name="SelectFileError" xml:space="preserve">
|
||||
<value>Por favor, selecciona un archivo para convertir.</value>
|
||||
</data>
|
||||
<data name="CopyButtonLabel" xml:space="preserve">
|
||||
<value>Copiar</value>
|
||||
</data>
|
||||
<data name="CopyButtonSuccess" xml:space="preserve">
|
||||
<value>¡Copiado!</value>
|
||||
</data>
|
||||
<data name="InvalidHeicFileError" xml:space="preserve">
|
||||
<value>Archivo HEIC inválido o formato no compatible. Revisa el archivo o intenta con otro. La biblioteca nativa 'libheif' podría faltar en el sistema.</value>
|
||||
</data>
|
||||
<data name="InvalidJpgFileError" xml:space="preserve">
|
||||
<value>Archivo JPG inválido o formato no compatible. Verifica que el archivo sea una imagen JPG válida.</value>
|
||||
</data>
|
||||
<data name="InvalidPdfFileError" xml:space="preserve">
|
||||
<value>Archivo PDF inválido, dañado o con protecciones no compatibles.</value>
|
||||
</data>
|
||||
<data name="PdfPasswordRequired" xml:space="preserve">
|
||||
<value>Este PDF está protegido. Ingresa la contraseña para continuar.</value>
|
||||
</data>
|
||||
<data name="PdfInvalidPassword" xml:space="preserve">
|
||||
<value>La contraseña ingresada no pudo desbloquear el PDF.</value>
|
||||
</data>
|
||||
<data name="PdfNoBarcodeFound" xml:space="preserve">
|
||||
<value>No encontramos una línea digitada en este PDF.</value>
|
||||
</data>
|
||||
<data name="PdfPreviewTooLarge" xml:space="preserve">
|
||||
<value>El contenido es demasiado grande para la vista previa. Descarga el resultado directamente.</value>
|
||||
</data>
|
||||
<!-- JPG to WebP Converter Resources -->
|
||||
<data name="JpgWebpConverterPageTitle" xml:space="preserve">
|
||||
<value>Convertidor JPG ↔ WebP Online Gratis</value>
|
||||
@ -270,4 +454,4 @@
|
||||
<data name="ProcessingError" xml:space="preserve">
|
||||
<value>Ocurrió un error al procesar tu solicitud. Inténtalo de nuevo más tarde.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
||||
@ -51,12 +51,21 @@
|
||||
<data name="ImageMenuTitle" xml:space="preserve">
|
||||
<value>Imagen</value>
|
||||
</data>
|
||||
<data name="DocumentMenuTitle" xml:space="preserve">
|
||||
<value>Documentos</value>
|
||||
</data>
|
||||
<data name="CaseConverterTitle" xml:space="preserve">
|
||||
<value>Convertidor de Mayúsculas/Minúsculas</value>
|
||||
</data>
|
||||
<data name="JpgToWebpTitle" xml:space="preserve">
|
||||
<value>JPG a WebP</value>
|
||||
</data>
|
||||
<data name="PdfToTextTitle" xml:space="preserve">
|
||||
<value>PDF a Texto/Markdown</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeTitle" xml:space="preserve">
|
||||
<value>Detectar línea digitada</value>
|
||||
</data>
|
||||
|
||||
<!-- Footer -->
|
||||
<data name="FooterText" xml:space="preserve">
|
||||
@ -113,12 +122,21 @@
|
||||
<data name="UrlImageConverters" xml:space="preserve">
|
||||
<value>convertidores-de-imagen</value>
|
||||
</data>
|
||||
<data name="UrlDocumentConverters" xml:space="preserve">
|
||||
<value>convertidores-de-documento</value>
|
||||
</data>
|
||||
<data name="UrlCaseConverter" xml:space="preserve">
|
||||
<value>conversor-de-mayusculas-minusculas</value>
|
||||
</data>
|
||||
<data name="UrlJpgToWebp" xml:space="preserve">
|
||||
<value>jpg-a-webp</value>
|
||||
</data>
|
||||
<data name="UrlPdfToText" xml:space="preserve">
|
||||
<value>pdf-a-texto</value>
|
||||
</data>
|
||||
<data name="UrlPdfBarcode" xml:space="preserve">
|
||||
<value>linea-digitada-desde-pdf</value>
|
||||
</data>
|
||||
|
||||
<!-- New individual converter resources - Mexican Spanish -->
|
||||
<data name="AllConvertersTitle" xml:space="preserve">
|
||||
@ -142,6 +160,18 @@
|
||||
<data name="HeicToJpgIndividualDescription" xml:space="preserve">
|
||||
<value>Convierte fotos HEIC del iPhone al formato JPG universalmente compatible.</value>
|
||||
</data>
|
||||
<data name="PdfToTextIndividualTitle" xml:space="preserve">
|
||||
<value>Extraer texto de PDF</value>
|
||||
</data>
|
||||
<data name="PdfToTextIndividualDescription" xml:space="preserve">
|
||||
<value>Transforma tus PDFs en texto simple o Markdown, incluso cuando requieren contraseña.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeIndividualTitle" xml:space="preserve">
|
||||
<value>Detectar línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeIndividualDescription" xml:space="preserve">
|
||||
<value>Obtén la línea digitada de boletos directamente desde el PDF, con soporte para archivos protegidos.</value>
|
||||
</data>
|
||||
|
||||
<!-- HEIC converter menu item -->
|
||||
<data name="HeicToJpgTitle" xml:space="preserve">
|
||||
@ -204,16 +234,167 @@
|
||||
<value>No hay límites en la cantidad de conversiones. Para garantizar un buen rendimiento, recomendamos archivos de hasta 10MB. El procesamiento es más rápido en computadoras modernas.</value>
|
||||
</data>
|
||||
|
||||
<!-- PDF to Text Converter Resources -->
|
||||
<data name="PdfTextConverterPageTitle" xml:space="preserve">
|
||||
<value>Extraer texto de PDF</value>
|
||||
</data>
|
||||
<data name="PdfTextConverterPageDescription" xml:space="preserve">
|
||||
<value>Convierte tus PDFs en texto simple o Markdown con soporte para archivos protegidos con contraseña.</value>
|
||||
</data>
|
||||
<data name="PdfPlainTextTabTitle" xml:space="preserve">
|
||||
<value>PDF → Texto simple</value>
|
||||
</data>
|
||||
<data name="PdfMarkdownTabTitle" xml:space="preserve">
|
||||
<value>PDF → Markdown</value>
|
||||
</data>
|
||||
<data name="PdfFileInputLabel" xml:space="preserve">
|
||||
<value>Seleccionar archivo PDF</value>
|
||||
</data>
|
||||
<data name="PdfPasswordLabel" xml:space="preserve">
|
||||
<value>Contraseña del PDF</value>
|
||||
</data>
|
||||
<data name="PdfPasswordPlaceholder" xml:space="preserve">
|
||||
<value>Ingresa la contraseña si el PDF está protegido</value>
|
||||
</data>
|
||||
<data name="PdfPasswordHint" xml:space="preserve">
|
||||
<value>Si el PDF requiere contraseña de apertura, escríbela aquí. De lo contrario, deja el campo vacío.</value>
|
||||
</data>
|
||||
<data name="ExtractPlainTextButton" xml:space="preserve">
|
||||
<value>Extraer y visualizar</value>
|
||||
</data>
|
||||
<data name="ExtractMarkdownButton" xml:space="preserve">
|
||||
<value>Extraer en Markdown</value>
|
||||
</data>
|
||||
<data name="DownloadPlainTextButton" xml:space="preserve">
|
||||
<value>Descargar texto</value>
|
||||
</data>
|
||||
<data name="DownloadMarkdownButton" xml:space="preserve">
|
||||
<value>Descargar Markdown</value>
|
||||
</data>
|
||||
<data name="PdfTextPreviewTitle" xml:space="preserve">
|
||||
<value>Vista previa del contenido</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhatTitle" xml:space="preserve">
|
||||
<value>¿Qué hace este conversor?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhatContent" xml:space="preserve">
|
||||
<value>Extrae el contenido textual del PDF y lo entrega como texto simple o Markdown, preservando cortes y secciones siempre que sea posible.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqHowTitle" xml:space="preserve">
|
||||
<value>¿Cómo usar el conversor?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqHowContent" xml:space="preserve">
|
||||
<value>Selecciona el archivo PDF, ingresa la contraseña si es necesario y elige la pestaña deseada. Visualiza el resultado en línea o descarga el archivo.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhyTitle" xml:space="preserve">
|
||||
<value>¿Por qué convertir PDF a texto o Markdown?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhyContent" xml:space="preserve">
|
||||
<value>El texto simple permite ediciones rápidas, mientras que Markdown facilita reutilizar el contenido en blogs o documentación.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqSecurityTitle" xml:space="preserve">
|
||||
<value>¿Es seguro subir mis PDFs?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqSecurityContent" xml:space="preserve">
|
||||
<value>Sí. Procesamos los archivos siguiendo estrictas políticas de privacidad y los eliminamos tras la conversión.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqLimitsTitle" xml:space="preserve">
|
||||
<value>¿Existen límites de tamaño?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqLimitsContent" xml:space="preserve">
|
||||
<value>Para la vista previa recomendamos PDFs de hasta 10MB. Archivos mayores pueden descargarse directamente en texto o Markdown.</value>
|
||||
</data>
|
||||
|
||||
<!-- PDF Barcode Converter Resources -->
|
||||
<data name="PdfBarcodeConverterPageTitle" xml:space="preserve">
|
||||
<value>Extraer línea digitada de PDF</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeConverterPageDescription" xml:space="preserve">
|
||||
<value>Detecta la línea digitada de boletos o facturas directamente desde el PDF, incluso si está protegido.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeDetectTabTitle" xml:space="preserve">
|
||||
<value>PDF → Ver línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeDownloadTabTitle" xml:space="preserve">
|
||||
<value>PDF → Descargar línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFileInputLabel" xml:space="preserve">
|
||||
<value>Seleccionar PDF del boleto</value>
|
||||
</data>
|
||||
<data name="PdfBarcodePasswordHint" xml:space="preserve">
|
||||
<value>Si el PDF está protegido con contraseña, ingrésala para permitir la lectura.</value>
|
||||
</data>
|
||||
<data name="DetectBarcodeButton" xml:space="preserve">
|
||||
<value>Detectar línea digitada</value>
|
||||
</data>
|
||||
<data name="DownloadBarcodeButton" xml:space="preserve">
|
||||
<value>Descargar línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodePreviewTitle" xml:space="preserve">
|
||||
<value>Línea digitada detectada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhatTitle" xml:space="preserve">
|
||||
<value>¿Qué es la línea digitada?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhatContent" xml:space="preserve">
|
||||
<value>Es la secuencia numérica del código de barras del boleto, utilizada para realizar pagos en bancos y aplicaciones.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqHowTitle" xml:space="preserve">
|
||||
<value>¿Cómo funciona el conversor?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqHowContent" xml:space="preserve">
|
||||
<value>Extrae el texto del PDF, identifica patrones de boletos (47 o 48 dígitos) y muestra la línea lista para copiar o descargar.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhyTitle" xml:space="preserve">
|
||||
<value>¿Cuándo usar este recurso?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhyContent" xml:space="preserve">
|
||||
<value>Ideal para boletos sin código de barras legible o cuando necesitas copiar la línea digitada para pagar en línea.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqSecurityTitle" xml:space="preserve">
|
||||
<value>¿Mis datos están seguros?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqSecurityContent" xml:space="preserve">
|
||||
<value>Sí. El PDF se procesa solo para extraer la línea digitada y se descarta inmediatamente después.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqLimitsTitle" xml:space="preserve">
|
||||
<value>¿Existen limitaciones?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqLimitsContent" xml:space="preserve">
|
||||
<value>Para mejores resultados utiliza PDFs con texto seleccionable. Boletos en formato de imagen pueden requerir una solución OCR externa.</value>
|
||||
</data>
|
||||
|
||||
<!-- Error Messages -->
|
||||
<data name="SelectFileError" xml:space="preserve">
|
||||
<value>Por favor, selecciona un archivo para convertir.</value>
|
||||
</data>
|
||||
<data name="CopyButtonLabel" xml:space="preserve">
|
||||
<value>Copiar</value>
|
||||
</data>
|
||||
<data name="CopyButtonSuccess" xml:space="preserve">
|
||||
<value>¡Copiado!</value>
|
||||
</data>
|
||||
<data name="InvalidHeicFileError" xml:space="preserve">
|
||||
<value>Archivo HEIC inválido o formato no compatible. Revisa el archivo o intenta con otro. La biblioteca nativa 'libheif' podría faltar en el sistema.</value>
|
||||
</data>
|
||||
<data name="InvalidJpgFileError" xml:space="preserve">
|
||||
<value>Archivo JPG inválido o formato no compatible. Verifica que el archivo sea una imagen JPG válida.</value>
|
||||
</data>
|
||||
<data name="InvalidPdfFileError" xml:space="preserve">
|
||||
<value>Archivo PDF inválido, dañado o con protecciones no compatibles.</value>
|
||||
</data>
|
||||
<data name="PdfPasswordRequired" xml:space="preserve">
|
||||
<value>Este PDF está protegido. Ingresa la contraseña para continuar.</value>
|
||||
</data>
|
||||
<data name="PdfInvalidPassword" xml:space="preserve">
|
||||
<value>La contraseña ingresada no pudo desbloquear el PDF.</value>
|
||||
</data>
|
||||
<data name="PdfNoBarcodeFound" xml:space="preserve">
|
||||
<value>No encontramos una línea digitada en este PDF.</value>
|
||||
</data>
|
||||
<data name="PdfPreviewTooLarge" xml:space="preserve">
|
||||
<value>El contenido es demasiado grande para la vista previa. Descarga el resultado directamente.</value>
|
||||
</data>
|
||||
<!-- JPG to WebP Converter Resources -->
|
||||
<data name="JpgWebpConverterPageTitle" xml:space="preserve">
|
||||
<value>Convertidor JPG ↔ WebP Online Gratis</value>
|
||||
@ -270,4 +451,4 @@
|
||||
<data name="ProcessingError" xml:space="preserve">
|
||||
<value>Ocurrió un error al procesar tu solicitud. Inténtalo de nuevo más tarde.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
||||
@ -51,12 +51,21 @@
|
||||
<data name="ImageMenuTitle" xml:space="preserve">
|
||||
<value>Imagen</value>
|
||||
</data>
|
||||
<data name="DocumentMenuTitle" xml:space="preserve">
|
||||
<value>Documentos</value>
|
||||
</data>
|
||||
<data name="CaseConverterTitle" xml:space="preserve">
|
||||
<value>Convertidor de Mayúsculas/Minúsculas</value>
|
||||
</data>
|
||||
<data name="JpgToWebpTitle" xml:space="preserve">
|
||||
<value>JPG a WebP</value>
|
||||
</data>
|
||||
<data name="PdfToTextTitle" xml:space="preserve">
|
||||
<value>PDF a Texto/Markdown</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeTitle" xml:space="preserve">
|
||||
<value>Detectar línea digitada</value>
|
||||
</data>
|
||||
|
||||
<!-- Footer -->
|
||||
<data name="FooterText" xml:space="preserve">
|
||||
@ -113,12 +122,21 @@
|
||||
<data name="UrlImageConverters" xml:space="preserve">
|
||||
<value>convertidores-de-imagen</value>
|
||||
</data>
|
||||
<data name="UrlDocumentConverters" xml:space="preserve">
|
||||
<value>convertidores-de-documento</value>
|
||||
</data>
|
||||
<data name="UrlCaseConverter" xml:space="preserve">
|
||||
<value>conversor-de-mayusculas-minusculas</value>
|
||||
</data>
|
||||
<data name="UrlJpgToWebp" xml:space="preserve">
|
||||
<value>jpg-a-webp</value>
|
||||
</data>
|
||||
<data name="UrlPdfToText" xml:space="preserve">
|
||||
<value>pdf-a-texto</value>
|
||||
</data>
|
||||
<data name="UrlPdfBarcode" xml:space="preserve">
|
||||
<value>linea-digitada-desde-pdf</value>
|
||||
</data>
|
||||
|
||||
<!-- New individual converter resources - Mexican Spanish -->
|
||||
<data name="AllConvertersTitle" xml:space="preserve">
|
||||
@ -142,6 +160,18 @@
|
||||
<data name="HeicToJpgIndividualDescription" xml:space="preserve">
|
||||
<value>Convierte fotos HEIC del iPhone al formato JPG universalmente compatible.</value>
|
||||
</data>
|
||||
<data name="PdfToTextIndividualTitle" xml:space="preserve">
|
||||
<value>Extraer texto de PDF</value>
|
||||
</data>
|
||||
<data name="PdfToTextIndividualDescription" xml:space="preserve">
|
||||
<value>Transforma tus PDFs en texto simple o Markdown, incluso cuando requieren contraseña.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeIndividualTitle" xml:space="preserve">
|
||||
<value>Detectar línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeIndividualDescription" xml:space="preserve">
|
||||
<value>Obtén la línea digitada de boletos directamente desde el PDF, con soporte para archivos protegidos.</value>
|
||||
</data>
|
||||
|
||||
<!-- HEIC converter menu item -->
|
||||
<data name="HeicToJpgTitle" xml:space="preserve">
|
||||
@ -204,16 +234,167 @@
|
||||
<value>No hay límites en la cantidad de conversiones. Para garantizar un buen rendimiento, recomendamos archivos de hasta 10MB. El procesamiento es más rápido en computadoras modernas.</value>
|
||||
</data>
|
||||
|
||||
<!-- PDF to Text Converter Resources -->
|
||||
<data name="PdfTextConverterPageTitle" xml:space="preserve">
|
||||
<value>Extraer texto de PDF</value>
|
||||
</data>
|
||||
<data name="PdfTextConverterPageDescription" xml:space="preserve">
|
||||
<value>Convierte tus PDFs en texto simple o Markdown con soporte para archivos protegidos con contraseña.</value>
|
||||
</data>
|
||||
<data name="PdfPlainTextTabTitle" xml:space="preserve">
|
||||
<value>PDF → Texto simple</value>
|
||||
</data>
|
||||
<data name="PdfMarkdownTabTitle" xml:space="preserve">
|
||||
<value>PDF → Markdown</value>
|
||||
</data>
|
||||
<data name="PdfFileInputLabel" xml:space="preserve">
|
||||
<value>Seleccionar archivo PDF</value>
|
||||
</data>
|
||||
<data name="PdfPasswordLabel" xml:space="preserve">
|
||||
<value>Contraseña del PDF</value>
|
||||
</data>
|
||||
<data name="PdfPasswordPlaceholder" xml:space="preserve">
|
||||
<value>Ingresa la contraseña si el PDF está protegido</value>
|
||||
</data>
|
||||
<data name="PdfPasswordHint" xml:space="preserve">
|
||||
<value>Si el PDF requiere contraseña de apertura, escríbela aquí. De lo contrario, deja el campo vacío.</value>
|
||||
</data>
|
||||
<data name="ExtractPlainTextButton" xml:space="preserve">
|
||||
<value>Extraer y visualizar</value>
|
||||
</data>
|
||||
<data name="ExtractMarkdownButton" xml:space="preserve">
|
||||
<value>Extraer en Markdown</value>
|
||||
</data>
|
||||
<data name="DownloadPlainTextButton" xml:space="preserve">
|
||||
<value>Descargar texto</value>
|
||||
</data>
|
||||
<data name="DownloadMarkdownButton" xml:space="preserve">
|
||||
<value>Descargar Markdown</value>
|
||||
</data>
|
||||
<data name="PdfTextPreviewTitle" xml:space="preserve">
|
||||
<value>Vista previa del contenido</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhatTitle" xml:space="preserve">
|
||||
<value>¿Qué hace este conversor?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhatContent" xml:space="preserve">
|
||||
<value>Extrae el contenido textual del PDF y lo entrega como texto simple o Markdown, preservando cortes y secciones siempre que sea posible.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqHowTitle" xml:space="preserve">
|
||||
<value>¿Cómo usar el conversor?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqHowContent" xml:space="preserve">
|
||||
<value>Selecciona el archivo PDF, ingresa la contraseña si es necesario y elige la pestaña deseada. Visualiza el resultado en línea o descarga el archivo.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhyTitle" xml:space="preserve">
|
||||
<value>¿Por qué convertir PDF a texto o Markdown?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhyContent" xml:space="preserve">
|
||||
<value>El texto simple permite ediciones rápidas, mientras que Markdown facilita reutilizar el contenido en blogs o documentación.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqSecurityTitle" xml:space="preserve">
|
||||
<value>¿Es seguro subir mis PDFs?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqSecurityContent" xml:space="preserve">
|
||||
<value>Sí. Procesamos los archivos siguiendo estrictas políticas de privacidad y los eliminamos tras la conversión.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqLimitsTitle" xml:space="preserve">
|
||||
<value>¿Existen límites de tamaño?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqLimitsContent" xml:space="preserve">
|
||||
<value>Para la vista previa recomendamos PDFs de hasta 10MB. Archivos mayores pueden descargarse directamente en texto o Markdown.</value>
|
||||
</data>
|
||||
|
||||
<!-- PDF Barcode Converter Resources -->
|
||||
<data name="PdfBarcodeConverterPageTitle" xml:space="preserve">
|
||||
<value>Extraer línea digitada de PDF</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeConverterPageDescription" xml:space="preserve">
|
||||
<value>Detecta la línea digitada de boletos o facturas directamente desde el PDF, incluso si está protegido.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeDetectTabTitle" xml:space="preserve">
|
||||
<value>PDF → Ver línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeDownloadTabTitle" xml:space="preserve">
|
||||
<value>PDF → Descargar línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFileInputLabel" xml:space="preserve">
|
||||
<value>Seleccionar PDF del boleto</value>
|
||||
</data>
|
||||
<data name="PdfBarcodePasswordHint" xml:space="preserve">
|
||||
<value>Si el PDF está protegido con contraseña, ingrésala para permitir la lectura.</value>
|
||||
</data>
|
||||
<data name="DetectBarcodeButton" xml:space="preserve">
|
||||
<value>Detectar línea digitada</value>
|
||||
</data>
|
||||
<data name="DownloadBarcodeButton" xml:space="preserve">
|
||||
<value>Descargar línea digitada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodePreviewTitle" xml:space="preserve">
|
||||
<value>Línea digitada detectada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhatTitle" xml:space="preserve">
|
||||
<value>¿Qué es la línea digitada?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhatContent" xml:space="preserve">
|
||||
<value>Es la secuencia numérica del código de barras del boleto, utilizada para realizar pagos en bancos y aplicaciones.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqHowTitle" xml:space="preserve">
|
||||
<value>¿Cómo funciona el conversor?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqHowContent" xml:space="preserve">
|
||||
<value>Extrae el texto del PDF, identifica patrones de boletos (47 o 48 dígitos) y muestra la línea lista para copiar o descargar.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhyTitle" xml:space="preserve">
|
||||
<value>¿Cuándo usar este recurso?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhyContent" xml:space="preserve">
|
||||
<value>Ideal para boletos sin código de barras legible o cuando necesitas copiar la línea digitada para pagar en línea.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqSecurityTitle" xml:space="preserve">
|
||||
<value>¿Mis datos están seguros?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqSecurityContent" xml:space="preserve">
|
||||
<value>Sí. El PDF se procesa solo para extraer la línea digitada y se descarta inmediatamente después.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqLimitsTitle" xml:space="preserve">
|
||||
<value>¿Existen limitaciones?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqLimitsContent" xml:space="preserve">
|
||||
<value>Para mejores resultados utiliza PDFs con texto seleccionable. Boletos en formato de imagen pueden requerir una solución OCR externa.</value>
|
||||
</data>
|
||||
|
||||
<!-- Error Messages -->
|
||||
<data name="SelectFileError" xml:space="preserve">
|
||||
<value>Por favor, selecciona un archivo para convertir.</value>
|
||||
</data>
|
||||
<data name="CopyButtonLabel" xml:space="preserve">
|
||||
<value>Copiar</value>
|
||||
</data>
|
||||
<data name="CopyButtonSuccess" xml:space="preserve">
|
||||
<value>¡Copiado!</value>
|
||||
</data>
|
||||
<data name="InvalidHeicFileError" xml:space="preserve">
|
||||
<value>Archivo HEIC inválido o formato no compatible. Revisa el archivo o intenta con otro. La biblioteca nativa 'libheif' podría faltar en el sistema.</value>
|
||||
</data>
|
||||
<data name="InvalidJpgFileError" xml:space="preserve">
|
||||
<value>Archivo JPG inválido o formato no compatible. Verifica que el archivo sea una imagen JPG válida.</value>
|
||||
</data>
|
||||
<data name="InvalidPdfFileError" xml:space="preserve">
|
||||
<value>Archivo PDF inválido, dañado o con protecciones no compatibles.</value>
|
||||
</data>
|
||||
<data name="PdfPasswordRequired" xml:space="preserve">
|
||||
<value>Este PDF está protegido. Ingresa la contraseña para continuar.</value>
|
||||
</data>
|
||||
<data name="PdfInvalidPassword" xml:space="preserve">
|
||||
<value>La contraseña ingresada no pudo desbloquear el PDF.</value>
|
||||
</data>
|
||||
<data name="PdfNoBarcodeFound" xml:space="preserve">
|
||||
<value>No encontramos una línea digitada en este PDF.</value>
|
||||
</data>
|
||||
<data name="PdfPreviewTooLarge" xml:space="preserve">
|
||||
<value>El contenido es demasiado grande para la vista previa. Descarga el resultado directamente.</value>
|
||||
</data>
|
||||
<!-- JPG to WebP Converter Resources -->
|
||||
<data name="JpgWebpConverterPageTitle" xml:space="preserve">
|
||||
<value>Convertidor JPG ↔ WebP Online Gratis</value>
|
||||
@ -270,4 +451,4 @@
|
||||
<data name="ProcessingError" xml:space="preserve">
|
||||
<value>Ocurrió un error al procesar tu solicitud. Inténtalo de nuevo más tarde.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
||||
@ -183,12 +183,21 @@
|
||||
<data name="ImageMenuTitle" xml:space="preserve">
|
||||
<value>Imagem</value>
|
||||
</data>
|
||||
<data name="DocumentMenuTitle" xml:space="preserve">
|
||||
<value>Documentos</value>
|
||||
</data>
|
||||
<data name="CaseConverterTitle" xml:space="preserve">
|
||||
<value>Conversor de Maiúsculas/Minúsculas</value>
|
||||
</data>
|
||||
<data name="JpgToWebpTitle" xml:space="preserve">
|
||||
<value>JPG para WebP</value>
|
||||
</data>
|
||||
<data name="PdfToTextTitle" xml:space="preserve">
|
||||
<value>PDF para Texto/Markdown</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeTitle" xml:space="preserve">
|
||||
<value>Extrair Linha Digitável</value>
|
||||
</data>
|
||||
|
||||
<!-- Footer Resources -->
|
||||
<data name="FooterText" xml:space="preserve">
|
||||
@ -211,12 +220,24 @@
|
||||
<data name="UrlImageConverters" xml:space="preserve">
|
||||
<value>conversores-de-imagem</value>
|
||||
</data>
|
||||
<data name="UrlDocumentConverters" xml:space="preserve">
|
||||
<value>conversores-de-documento</value>
|
||||
</data>
|
||||
<data name="UrlCaseConverter" xml:space="preserve">
|
||||
<value>conversor-de-maiusculas-minusculas</value>
|
||||
</data>
|
||||
<data name="UrlJpgToWebp" xml:space="preserve">
|
||||
<value>jpg-para-webp</value>
|
||||
</data>
|
||||
<data name="UrlHeicToJpg" xml:space="preserve">
|
||||
<value>heic-para-jpg</value>
|
||||
</data>
|
||||
<data name="UrlPdfToText" xml:space="preserve">
|
||||
<value>pdf-para-texto</value>
|
||||
</data>
|
||||
<data name="UrlPdfBarcode" xml:space="preserve">
|
||||
<value>linha-digitavel-do-pdf</value>
|
||||
</data>
|
||||
|
||||
<!-- FAQ Resources for Contact Page -->
|
||||
<data name="Faq1Question" xml:space="preserve">
|
||||
@ -254,6 +275,18 @@
|
||||
<data name="HeicToJpgIndividualDescription" xml:space="preserve">
|
||||
<value>Converta fotos HEIC do iPhone para o formato JPG universalmente compatível.</value>
|
||||
</data>
|
||||
<data name="PdfToTextIndividualTitle" xml:space="preserve">
|
||||
<value>Extrair Texto de PDF</value>
|
||||
</data>
|
||||
<data name="PdfToTextIndividualDescription" xml:space="preserve">
|
||||
<value>Transforme PDFs protegidos ou não em texto simples ou Markdown em poucos segundos.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeIndividualTitle" xml:space="preserve">
|
||||
<value>Detectar Linha Digitável</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeIndividualDescription" xml:space="preserve">
|
||||
<value>Identifique a linha digitável de boletos diretamente do PDF, mesmo quando há proteção por senha.</value>
|
||||
</data>
|
||||
|
||||
<!-- HEIC converter menu item -->
|
||||
<data name="HeicToJpgTitle" xml:space="preserve">
|
||||
@ -316,6 +349,136 @@
|
||||
<value>Não há limites para a quantidade de conversões. Para garantir um bom desempenho, recomendamos arquivos de até 10MB. O processamento é mais rápido em computadores modernos.</value>
|
||||
</data>
|
||||
|
||||
<!-- PDF to Text Converter Resources -->
|
||||
<data name="PdfTextConverterPageTitle" xml:space="preserve">
|
||||
<value>Extrair Texto de PDF</value>
|
||||
</data>
|
||||
<data name="PdfTextConverterPageDescription" xml:space="preserve">
|
||||
<value>Converta seus PDFs em texto simples ou Markdown, com suporte a arquivos protegidos por senha.</value>
|
||||
</data>
|
||||
<data name="PdfPlainTextTabTitle" xml:space="preserve">
|
||||
<value>PDF → Texto Simples</value>
|
||||
</data>
|
||||
<data name="PdfMarkdownTabTitle" xml:space="preserve">
|
||||
<value>PDF → Markdown</value>
|
||||
</data>
|
||||
<data name="PdfFileInputLabel" xml:space="preserve">
|
||||
<value>Selecionar arquivo PDF</value>
|
||||
</data>
|
||||
<data name="PdfPasswordLabel" xml:space="preserve">
|
||||
<value>Senha do PDF</value>
|
||||
</data>
|
||||
<data name="PdfPasswordPlaceholder" xml:space="preserve">
|
||||
<value>Informe a senha, se o PDF estiver protegido</value>
|
||||
</data>
|
||||
<data name="PdfPasswordHint" xml:space="preserve">
|
||||
<value>Se o PDF exigir senha de abertura, digite-a abaixo. Caso contrário, deixe em branco.</value>
|
||||
</data>
|
||||
<data name="ExtractPlainTextButton" xml:space="preserve">
|
||||
<value>Extrair e Visualizar</value>
|
||||
</data>
|
||||
<data name="ExtractMarkdownButton" xml:space="preserve">
|
||||
<value>Extrair em Markdown</value>
|
||||
</data>
|
||||
<data name="DownloadPlainTextButton" xml:space="preserve">
|
||||
<value>Baixar Texto</value>
|
||||
</data>
|
||||
<data name="DownloadMarkdownButton" xml:space="preserve">
|
||||
<value>Baixar Markdown</value>
|
||||
</data>
|
||||
<data name="PdfTextPreviewTitle" xml:space="preserve">
|
||||
<value>Pré-visualização do Conteúdo</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhatTitle" xml:space="preserve">
|
||||
<value>O que faz este conversor PDF para Texto?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhatContent" xml:space="preserve">
|
||||
<value>Ele extrai o conteúdo textual do seu PDF e o disponibiliza como texto simples ou Markdown, preservando quebras de linha e seções sempre que possível.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqHowTitle" xml:space="preserve">
|
||||
<value>Como usar o conversor?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqHowContent" xml:space="preserve">
|
||||
<value>Selecione o arquivo PDF, informe a senha se necessário e escolha a aba desejada. Visualize o resultado online ou faça o download direto.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhyTitle" xml:space="preserve">
|
||||
<value>Por que converter PDF para texto ou Markdown?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqWhyContent" xml:space="preserve">
|
||||
<value>Texto simples facilita edições rápidas, enquanto Markdown permite reaproveitar o conteúdo em blogs, documentações e geradores de site estático.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqSecurityTitle" xml:space="preserve">
|
||||
<value>É seguro enviar meus PDFs?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqSecurityContent" xml:space="preserve">
|
||||
<value>Sim. O processamento ocorre seguindo políticas rígidas de privacidade e os arquivos são descartados após a conversão.</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqLimitsTitle" xml:space="preserve">
|
||||
<value>Existem limites de tamanho?</value>
|
||||
</data>
|
||||
<data name="PdfTextFaqLimitsContent" xml:space="preserve">
|
||||
<value>Para pré-visualização recomendamos PDFs de até 10MB. Conteúdos maiores podem ser baixados diretamente em texto ou Markdown.</value>
|
||||
</data>
|
||||
|
||||
<!-- PDF Barcode Converter Resources -->
|
||||
<data name="PdfBarcodeConverterPageTitle" xml:space="preserve">
|
||||
<value>Extrair Linha Digitável de PDF</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeConverterPageDescription" xml:space="preserve">
|
||||
<value>Detecte a linha digitável de boletos ou faturas diretamente a partir do PDF, com suporte a arquivos protegidos.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeDetectTabTitle" xml:space="preserve">
|
||||
<value>PDF → Visualizar Linha Digitável</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeDownloadTabTitle" xml:space="preserve">
|
||||
<value>PDF → Baixar Linha Digitável</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFileInputLabel" xml:space="preserve">
|
||||
<value>Selecionar PDF do boleto</value>
|
||||
</data>
|
||||
<data name="PdfBarcodePasswordHint" xml:space="preserve">
|
||||
<value>Caso o PDF esteja protegido por senha, informe-a abaixo para permitir a leitura.</value>
|
||||
</data>
|
||||
<data name="DetectBarcodeButton" xml:space="preserve">
|
||||
<value>Detectar Linha Digitável</value>
|
||||
</data>
|
||||
<data name="DownloadBarcodeButton" xml:space="preserve">
|
||||
<value>Baixar Linha Digitável</value>
|
||||
</data>
|
||||
<data name="PdfBarcodePreviewTitle" xml:space="preserve">
|
||||
<value>Linha Digitável Detectada</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhatTitle" xml:space="preserve">
|
||||
<value>O que é a linha digitável?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhatContent" xml:space="preserve">
|
||||
<value>É a sequência numérica que representa o código de barras de boletos e faturas, usada para pagamentos em bancos e aplicativos.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqHowTitle" xml:space="preserve">
|
||||
<value>Como o conversor funciona?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqHowContent" xml:space="preserve">
|
||||
<value>O sistema extrai o texto do PDF, identifica padrões numéricos de boletos (47 ou 48 dígitos) e exibe a linha digitável pronta para copiar ou baixar.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhyTitle" xml:space="preserve">
|
||||
<value>Quando usar este recurso?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqWhyContent" xml:space="preserve">
|
||||
<value>Ideal para boletos sem código de barras legível ou quando você precisa copiar a linha digitável para pagamento online rapidamente.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqSecurityTitle" xml:space="preserve">
|
||||
<value>Meus dados ficam seguros?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqSecurityContent" xml:space="preserve">
|
||||
<value>Sim. O PDF é processado apenas para extrair a linha digitável e o conteúdo é descartado logo após a operação.</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqLimitsTitle" xml:space="preserve">
|
||||
<value>Existe algum limite?</value>
|
||||
</data>
|
||||
<data name="PdfBarcodeFaqLimitsContent" xml:space="preserve">
|
||||
<value>Para melhores resultados, utilize PDFs com texto selecionável. Boletos apenas com imagem podem exigir uma solução OCR externa.</value>
|
||||
</data>
|
||||
|
||||
<!-- JPG to WebP Converter Resources -->
|
||||
<data name="JpgWebpConverterPageTitle" xml:space="preserve">
|
||||
<value>Conversor JPG ↔ WebP Online Grátis</value>
|
||||
@ -370,6 +533,12 @@
|
||||
<data name="UseConverterButton" xml:space="preserve">
|
||||
<value>Acessar Conversor</value>
|
||||
</data>
|
||||
<data name="CopyButtonLabel" xml:space="preserve">
|
||||
<value>Copiar</value>
|
||||
</data>
|
||||
<data name="CopyButtonSuccess" xml:space="preserve">
|
||||
<value>Copiado!</value>
|
||||
</data>
|
||||
|
||||
<!-- Error Messages -->
|
||||
<data name="SelectFileError" xml:space="preserve">
|
||||
@ -384,7 +553,22 @@
|
||||
<data name="JpgToHeicNotAvailableError" xml:space="preserve">
|
||||
<value>A conversão de JPG para HEIC ainda não está disponível. Esta funcionalidade será implementada em breve.</value>
|
||||
</data>
|
||||
<data name="InvalidPdfFileError" xml:space="preserve">
|
||||
<value>Arquivo PDF inválido, corrompido ou protegido por recursos não suportados.</value>
|
||||
</data>
|
||||
<data name="PdfPasswordRequired" xml:space="preserve">
|
||||
<value>Este PDF está protegido. Informe a senha para continuar.</value>
|
||||
</data>
|
||||
<data name="PdfInvalidPassword" xml:space="preserve">
|
||||
<value>A senha informada não conseguiu desbloquear o PDF.</value>
|
||||
</data>
|
||||
<data name="PdfNoBarcodeFound" xml:space="preserve">
|
||||
<value>Não encontramos uma linha digitável neste PDF.</value>
|
||||
</data>
|
||||
<data name="PdfPreviewTooLarge" xml:space="preserve">
|
||||
<value>O conteúdo é muito grande para pré-visualização. Faça o download direto.</value>
|
||||
</data>
|
||||
<data name="ProcessingError" xml:space="preserve">
|
||||
<value>Ocorreu um erro ao processar sua solicitação. Tente novamente mais tarde.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
||||
@ -1,11 +1,21 @@
|
||||
@using Microsoft.AspNetCore.Localization
|
||||
@using Microsoft.Extensions.Options
|
||||
@using Microsoft.Extensions.Configuration
|
||||
|
||||
@inject IOptions<RequestLocalizationOptions> LocOptions
|
||||
@inject IConfiguration Configuration
|
||||
|
||||
@{
|
||||
var requestCulture = Context.Features.Get<IRequestCultureFeature>();
|
||||
var supportedCultures = LocOptions.Value.SupportedCultures;
|
||||
|
||||
// Configuração de Ads
|
||||
var adSection = Configuration.GetSection("Ads");
|
||||
var adEnabled = adSection.GetValue<bool>("Enabled");
|
||||
var adProvider = adSection.GetValue<string>("Provider"); // "House" ou "Google"
|
||||
var googlePublisherId = adSection.GetValue<string>("GooglePublisherId");
|
||||
var slotTop = adSection.GetValue<string>("Slots:Top");
|
||||
var slotSide = adSection.GetValue<string>("Slots:Side");
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
@ -13,11 +23,18 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="@(ViewBag.MetaDescription ?? ViewBag.PageDescription ?? ViewBag.Subtitle ?? ViewBag.PageTitle ?? "Convert-It Online")" />
|
||||
<title>@ViewData["Title"] - Convert-It Online</title>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" href="~/css/site.css" />
|
||||
|
||||
@if (adEnabled && adProvider == "Google" && !string.IsNullOrEmpty(googlePublisherId))
|
||||
{
|
||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=@googlePublisherId"
|
||||
crossorigin="anonymous"></script>
|
||||
}
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
@ -42,6 +59,19 @@
|
||||
</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item dropdown mx-2">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="documentToolsDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-file-earmark-text me-1"></i>@ViewBag.DocumentMenuTitle
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="documentToolsDropdown">
|
||||
<li><a class="dropdown-item" href="@Html.LocalizedUrl("DocumentConverters", "PdfToText")">
|
||||
<i class="bi bi-file-text me-2"></i>@ViewBag.PdfToTextTitle
|
||||
</a></li>
|
||||
<li><a class="dropdown-item" href="@Html.LocalizedUrl("DocumentConverters", "PdfBarcodeLine")">
|
||||
<i class="bi bi-upc-scan me-2"></i>@ViewBag.PdfBarcodeTitle
|
||||
</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item dropdown mx-2">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="imageToolsDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-image-alt me-1"></i>@ViewBag.ImageMenuTitle
|
||||
@ -84,17 +114,98 @@
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div class="ad-placeholder ad-top d-none d-lg-flex">
|
||||
<span>Ad Placeholder (Top)</span>
|
||||
</div>
|
||||
<!-- AD TOP -->
|
||||
@if (adEnabled)
|
||||
{
|
||||
@if (adProvider == "House")
|
||||
{
|
||||
<a href="https://qrrapido.site" target="_blank" class="house-ad-top text-decoration-none d-none d-lg-block">
|
||||
<div class="ad-content">
|
||||
<div class="qr-animation">
|
||||
<div class="qr-box"></div>
|
||||
<div class="scan-line"></div>
|
||||
</div>
|
||||
<div class="ad-text">
|
||||
<span class="badge bg-warning text-dark mb-1">NOVO</span>
|
||||
<h5>Gerador de QR Code Ultrarrápido</h5>
|
||||
<p>Crie QR Codes para WiFi, Links e Pix em 1 segundo. Grátis!</p>
|
||||
</div>
|
||||
<div class="cta-button">
|
||||
Criar Agora <i class="bi bi-arrow-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
else if (adProvider == "Google" && !string.IsNullOrEmpty(slotTop))
|
||||
{
|
||||
<div class="d-none d-lg-block mb-4" style="min-height: 90px; text-align:center;">
|
||||
<!-- Topo -->
|
||||
<ins class="adsbygoogle"
|
||||
style="display:block"
|
||||
data-ad-client="@googlePublisherId"
|
||||
data-ad-slot="@slotTop"
|
||||
data-ad-format="auto"
|
||||
data-full-width-responsive="true"></ins>
|
||||
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- Placeholder visível apenas se não houver ad configurado e estiver em debug/dev se desejado -->
|
||||
<!-- <div class="ad-placeholder ad-top d-none d-lg-flex"><span>Ad Space</span></div> -->
|
||||
}
|
||||
}
|
||||
|
||||
<main role="main" class="pb-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
</div>
|
||||
<div class="col-lg-3 d-none d-lg-block">
|
||||
<div class="ad-placeholder ad-side">
|
||||
<span>Ad Placeholder (Side)</span>
|
||||
</div>
|
||||
<!-- AD SIDE -->
|
||||
@if (adEnabled)
|
||||
{
|
||||
@if (adProvider == "House")
|
||||
{
|
||||
<a href="https://jobmaker.com.br" target="_blank" class="house-ad-side text-decoration-none">
|
||||
<div class="terminal-header">
|
||||
<span class="dot red"></span><span class="dot yellow"></span><span class="dot green"></span>
|
||||
</div>
|
||||
<div class="ad-body">
|
||||
<div class="code-typing">
|
||||
<span class="comment">// Precisa integrar sistemas?</span><br>
|
||||
<span class="keyword">await</span> JobMaker.<span class="method">Automate</span>(legacy_sys);<br>
|
||||
<span class="success">>> Processo Otimizado!</span>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<h5>Especialistas em .NET e Integrações</h5>
|
||||
<ul class="list-unstyled text-small text-muted mt-2">
|
||||
<li><i class="bi bi-check2"></i> APIs & Microserviços</li>
|
||||
<li><i class="bi bi-check2"></i> Robôs de Dados</li>
|
||||
<li><i class="bi bi-check2"></i> Modernização de Legado</li>
|
||||
</ul>
|
||||
<button class="btn btn-outline-primary btn-sm w-100 mt-3">Fale Conosco</button>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
else if (adProvider == "Google" && !string.IsNullOrEmpty(slotSide))
|
||||
{
|
||||
<div class="mt-4 sticky-top" style="top: 2rem; min-height: 600px;">
|
||||
<!-- Lateral -->
|
||||
<ins class="adsbygoogle"
|
||||
style="display:block"
|
||||
data-ad-client="@googlePublisherId"
|
||||
data-ad-slot="@slotSide"
|
||||
data-ad-format="auto"
|
||||
data-full-width-responsive="true"></ins>
|
||||
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- <div class="ad-placeholder ad-side"><span>Ad Space</span></div> -->
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -117,4 +228,4 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@ -9,5 +9,14 @@
|
||||
"AllowedHosts": "*",
|
||||
"Serilog": {
|
||||
"OpenSearchUrl": ""
|
||||
},
|
||||
"Ads": {
|
||||
"Enabled": true,
|
||||
"Provider": "House",
|
||||
"GooglePublisherId": "pub-3475956393038764",
|
||||
"Slots": {
|
||||
"Top": "0000000000",
|
||||
"Side": "0000000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -430,4 +430,191 @@ h1, h2, h3, h4, h5, h6, p, div, span, a {
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ========== HOUSE ADS (BANNERS PRÓPRIOS) ========== */
|
||||
|
||||
/* Banner Topo - QR Rápido */
|
||||
.house-ad-top {
|
||||
display: block;
|
||||
background: linear-gradient(135deg, #6610f2 0%, #d63384 100%);
|
||||
border-radius: 1rem;
|
||||
padding: 1rem 2rem;
|
||||
color: white !important;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
margin-bottom: 2rem;
|
||||
border: none;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.house-ad-top:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(102, 16, 242, 0.3);
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.house-ad-top .ad-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.house-ad-top .qr-animation {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
|
||||
.house-ad-top .qr-box {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid white;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.house-ad-top .qr-box::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.house-ad-top .scan-line {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: #00ff00;
|
||||
top: 10%;
|
||||
left: 0;
|
||||
animation: scanMove 1.5s infinite alternate ease-in-out;
|
||||
box-shadow: 0 0 4px #00ff00;
|
||||
}
|
||||
|
||||
@keyframes scanMove {
|
||||
0% { top: 10%; }
|
||||
100% { top: 90%; }
|
||||
}
|
||||
|
||||
.house-ad-top .ad-text h5 {
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
font-size: 1.25rem;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.house-ad-top .ad-text p {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.9;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.house-ad-top .cta-button {
|
||||
background: white;
|
||||
color: #6610f2;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 2rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Banner Lateral - JobMaker */
|
||||
.house-ad-side {
|
||||
display: block;
|
||||
background-color: #0f172a; /* Slate 900 */
|
||||
border-radius: 1rem;
|
||||
border: 1px solid #1e293b;
|
||||
color: #e2e8f0 !important;
|
||||
overflow: hidden;
|
||||
position: sticky;
|
||||
top: 2rem;
|
||||
text-decoration: none !important;
|
||||
transition: border-color 0.3s ease;
|
||||
min-height: auto; /* Remove min-height fixo se necessário */
|
||||
}
|
||||
|
||||
.house-ad-side:hover {
|
||||
border-color: #3b82f6;
|
||||
color: #e2e8f0 !important;
|
||||
}
|
||||
|
||||
.house-ad-side .terminal-header {
|
||||
background: #1e293b;
|
||||
padding: 0.75rem 1rem;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
border-bottom: 1px solid #334155;
|
||||
}
|
||||
|
||||
.house-ad-side .dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.house-ad-side .dot.red { background: #ef4444; }
|
||||
.house-ad-side .dot.yellow { background: #f59e0b; }
|
||||
.house-ad-side .dot.green { background: #22c55e; }
|
||||
|
||||
.house-ad-side .ad-body {
|
||||
padding: 1.5rem;
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
}
|
||||
|
||||
.house-ad-side .code-typing {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.house-ad-side .comment { color: #64748b; }
|
||||
.house-ad-side .keyword { color: #c084fc; }
|
||||
.house-ad-side .method { color: #60a5fa; }
|
||||
.house-ad-side .success { color: #4ade80; display: block; margin-top: 0.5rem; font-weight: bold;}
|
||||
|
||||
.house-ad-side h5 {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
color: white !important;
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.house-ad-side .text-muted {
|
||||
color: #94a3b8 !important; /* Slate 400 */
|
||||
}
|
||||
|
||||
.house-ad-side .btn-outline-primary {
|
||||
border-color: #3b82f6;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.house-ad-side .btn-outline-primary:hover {
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Responsividade para o House Ad Top */
|
||||
@media (max-width: 992px) {
|
||||
.house-ad-top {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user